001    package railo.runtime.tag;
002    
003    import java.io.ByteArrayInputStream;
004    import java.io.FileNotFoundException;
005    import java.io.IOException;
006    import java.io.InputStream;
007    import java.io.PrintStream;
008    import java.io.UnsupportedEncodingException;
009    import java.net.MalformedURLException;
010    import java.net.URL;
011    import java.util.ArrayList;
012    import java.util.zip.GZIPInputStream;
013    
014    import org.apache.commons.httpclient.Cookie;
015    import org.apache.commons.httpclient.Header;
016    import org.apache.commons.httpclient.HostConfiguration;
017    import org.apache.commons.httpclient.HttpClient;
018    import org.apache.commons.httpclient.HttpConnectionManager;
019    import org.apache.commons.httpclient.HttpMethod;
020    import org.apache.commons.httpclient.HttpState;
021    import org.apache.commons.httpclient.NameValuePair;
022    import org.apache.commons.httpclient.SimpleHttpConnectionManager;
023    import org.apache.commons.httpclient.UsernamePasswordCredentials;
024    import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
025    import org.apache.commons.httpclient.methods.DeleteMethod;
026    import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
027    import org.apache.commons.httpclient.methods.GetMethod;
028    import org.apache.commons.httpclient.methods.HeadMethod;
029    import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
030    import org.apache.commons.httpclient.methods.OptionsMethod;
031    import org.apache.commons.httpclient.methods.PostMethod;
032    import org.apache.commons.httpclient.methods.PutMethod;
033    import org.apache.commons.httpclient.methods.StringRequestEntity;
034    import org.apache.commons.httpclient.methods.TraceMethod;
035    import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
036    import org.apache.commons.httpclient.methods.multipart.Part;
037    import org.apache.commons.httpclient.params.HttpMethodParams;
038    import org.apache.commons.httpclient.util.EncodingUtil;
039    
040    import railo.commons.io.IOUtil;
041    import railo.commons.io.res.Resource;
042    import railo.commons.io.res.util.ResourceUtil;
043    import railo.commons.lang.StringUtil;
044    import railo.commons.net.CookieUtil;
045    import railo.commons.net.HTTPUtil;
046    import railo.commons.net.RailoStringPart;
047    import railo.commons.net.ResourcePart;
048    import railo.commons.net.ResourcePartSource;
049    import railo.commons.net.URLEncoder;
050    import railo.runtime.config.Config;
051    import railo.runtime.engine.ThreadLocalPageContext;
052    import railo.runtime.exp.ApplicationException;
053    import railo.runtime.exp.ExpressionException;
054    import railo.runtime.exp.HTTPException;
055    import railo.runtime.exp.NativeException;
056    import railo.runtime.exp.PageException;
057    import railo.runtime.ext.tag.BodyTagImpl;
058    import railo.runtime.net.http.ReqRspUtil;
059    import railo.runtime.op.Caster;
060    import railo.runtime.op.Decision;
061    import railo.runtime.text.csv.CSVParser;
062    import railo.runtime.type.Array;
063    import railo.runtime.type.ArrayImpl;
064    import railo.runtime.type.Collection.Key;
065    import railo.runtime.type.KeyImpl;
066    import railo.runtime.type.List;
067    import railo.runtime.type.Query;
068    import railo.runtime.type.Struct;
069    import railo.runtime.type.StructImpl;
070    import railo.runtime.type.util.ArrayUtil;
071    import railo.runtime.util.URLResolver;
072    
073    // MUST change behavor of mltiple headers now is a array, it das so?
074    
075    /**
076    * Lets you execute HTTP POST and GET operations on files. Using cfhttp, you can execute standard 
077    *   GET operations and create a query object from a text file. POST operations lets you upload MIME file 
078    *   types to a server, or post cookie, formfield, URL, file, or CGI variables directly to a specified server.
079    *
080    *
081    *
082    * 
083    **/
084    public final class Http extends BodyTagImpl {
085            
086    
087        /**
088         * Maximal count of redirects (5)
089         */
090        public static final short MAX_REDIRECT=15;
091        
092        /**
093         * Constant value for HTTP Status Code "moved Permanently 301"
094         */
095        public static final int STATUS_REDIRECT_MOVED_PERMANENTLY=301;
096        /**
097         * Constant value for HTTP Status Code "Found 302"
098         */
099        public static final int STATUS_REDIRECT_FOUND=302;
100        /**
101         * Constant value for HTTP Status Code "see other 303"
102         */
103        public static final int STATUS_REDIRECT_SEE_OTHER=303;
104        
105    
106        public static final int STATUS_REDIRECT_TEMPORARY_REDIRECT = 307;
107    
108    
109            
110        
111        
112    
113            private static final short METHOD_GET=0;
114            private static final short METHOD_POST=1;
115            private static final short METHOD_HEAD=2;
116            private static final short METHOD_PUT=3;
117            private static final short METHOD_DELETE=4;
118            private static final short METHOD_OPTIONS=5;
119            private static final short METHOD_TRACE=6;
120            
121            private static final String NO_MIMETYPE="Unable to determine MIME type of file.";
122            
123            private static final int STATUS_OK=200;
124            
125            private static final short GET_AS_BINARY_NO=0;
126            private static final short GET_AS_BINARY_YES=1;
127            private static final short GET_AS_BINARY_AUTO=2;
128    
129            private static final Key ERROR_DETAIL = KeyImpl.intern("errordetail");
130            private static final Key STATUSCODE = KeyImpl.intern("statuscode");
131            private static final Key STATUS_CODE = KeyImpl.intern("status_code");
132            private static final Key STATUS_TEXT = KeyImpl.intern("status_text");
133            private static final Key HTTP_VERSION = KeyImpl.intern("http_version");
134            
135    
136            private static final Key MIME_TYPE = KeyImpl.intern("mimetype");
137            private static final Key CHARSET = KeyImpl.intern("charset");
138            private static final Key FILE_CONTENT = KeyImpl.intern("filecontent");
139            private static final Key HEADER = KeyImpl.intern("header");
140            private static final Key TEXT = KeyImpl.intern("text");
141            private static final Key EXPLANATION = KeyImpl.intern("explanation");
142            private static final Key RESPONSEHEADER = KeyImpl.intern("responseheader");
143            private static final Key SET_COOKIE = KeyImpl.intern("set-cookie");
144    
145            
146            
147            
148            static {
149                //Protocol myhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
150                //Protocol.registerProtocol("https", new Protocol("https", new EasySSLProtocolSocketFactory(), 443));
151            }
152            
153    
154        private ArrayList<HttpParamBean> params=new ArrayList<HttpParamBean>();
155            
156            
157            /** When required by a server, a valid password. */
158            private String password;
159    
160            /** Required for creating a query. Options are a tab or comma. Default is a comma. */
161            private char delimiter=',';
162    
163            /** Yes or No. Default is No. For GET and POST operations, if Yes, page reference returned into the 
164            **      fileContent internal variable has its internal URLs fully resolved, including port number, so that 
165            **      links remain intact. */
166            private boolean resolveurl;
167    
168            /** A value, in seconds. When a URL timeout is specified in the browser */
169            private long timeout=-1;
170    
171            /** Host name or IP address of a proxy server. */
172            private String proxyserver;
173    
174            /** The filename to be used for the file that is accessed. For GET operations, defaults to the name 
175            **      pecified in url. Enter path information in the path attribute. */
176            private String strFile;
177    
178            /** The path to the directory in which a file is to be stored. If a path is not specified in a POST 
179            **      or GET operation, a variable is created (cfhttp.fileContent) that you can use to display the results 
180            **      of the POST operation in a cfoutput. */
181            private String strPath;
182    
183            /** Boolean indicating whether to throw an exception that can be caught by using the cftry and 
184            **      cfcatch tags. The default is NO. */
185            private boolean throwonerror;
186    
187            /** set the charset for the call. */
188            private String charset=null;
189    
190            /** The port number on the proxy server from which the object is requested. Default is 80. When 
191            **      used with resolveURL, the URLs of retrieved documents that specify a port number are automatically 
192            **      resolved to preserve links in the retrieved document. */
193            private int proxyport=80;
194    
195            /** Specifies the column names for a query when creating a query as a result of a cfhttp GET. */
196            private String[] columns;
197    
198            /** The port number on the server from which the object is requested. Default is 80. When used with 
199            **      resolveURL, the URLs of retrieved documents that specify a port number are automatically resolved to 
200            **      preserve links in the retrieved document. If a port number is specified in the url attribute, the port
201            **      value overrides the value of the port attribute. */
202            private int port=-1;
203    
204            /** User agent request header. */
205            private String useragent="Railo (CFML Engine)";
206    
207            /** Required for creating a query. Indicates the start and finish of a column. Should be 
208            **      appropriately escaped when embedded in a column. For example, if the qualifier is a double quotation 
209            **      mark, it should be escaped as """". If there is no text qualifier in the file, specify it as " ". 
210            **      Default is the double quotation mark ("). */
211            private char textqualifier='"';
212    
213            /** When required by a server, a valid username. */
214            private String username;
215    
216            /** Full URL of the host name or IP address of the server on which the file resides. The URL must be
217            **      an absolute URL, including the protocol (http or https) and hostname. It may optionally contain a port
218            **      number. Port numbers specified in the url attribute override the port attribute. */
219            private String url;
220    
221            /** Boolean indicating whether to redirect execution or stop execution.*/
222            private boolean redirect=true;
223    
224    
225            /** The name to assign to a query if the a query is constructed from a file. */
226            private String name;
227    
228            /** GET or POST. Use GET to download a text or binary file or to create a query from the contents 
229            **      of a text file. Use POST to send information to a server page or a CGI program for processing. POST 
230            **      requires the use of a cfhttpparam tag. */
231            private short method=METHOD_GET;
232    
233            //private boolean hasBody=false;
234            
235            private boolean firstrowasheaders=true;
236    
237            private String proxyuser=null;
238            private String proxypassword="";
239            private boolean multiPart=false;
240            private String multiPartType=MultipartRequestEntityFlex.MULTIPART_FORM_DATA;
241            
242            private short getAsBinary=GET_AS_BINARY_NO;
243        private String result="cfhttp";
244        
245        private boolean addtoken=false;
246    
247            
248            /**
249            * @see javax.servlet.jsp.tagext.Tag#release()
250            */
251            public void release()   {
252                    super.release();
253                params.clear();
254                    password=null;
255                    delimiter=',';
256                    resolveurl=false;
257                    timeout=-1L;
258                    proxyserver=null;
259                    proxyport=80;
260                    proxyuser=null;
261                    proxypassword="";
262                    strFile=null;
263                    throwonerror=false;
264                    charset=null;
265                    columns=null;
266                    port=-1;
267                    useragent="Railo (CFML Engine)";
268                    textqualifier='"';
269                    username=null;
270                    url=null;
271                    redirect=true;
272                    strPath=null;
273                    name=null;
274                    method=METHOD_GET;
275                    //hasBody=false;
276                    firstrowasheaders=true;
277                    
278                    getAsBinary=GET_AS_BINARY_NO;
279                    multiPart=false;
280                    multiPartType=MultipartRequestEntityFlex.MULTIPART_FORM_DATA;
281            result="cfhttp";
282            addtoken=false;
283            }
284            
285            /**
286             * @param firstrowasheaders
287             */
288            public void setFirstrowasheaders(boolean firstrowasheaders)     {
289                    this.firstrowasheaders=firstrowasheaders;
290            }
291    
292            /** set the value password
293            *  When required by a server, a valid password.
294            * @param password value to set
295            **/
296            public void setPassword(String password)        {
297                    this.password=password;
298            }
299            /** set the value password
300            *  When required by a proxy server, a valid password.
301            * @param proxypassword value to set
302            **/
303            public void setProxypassword(String proxypassword)      {
304                    this.proxypassword=proxypassword;
305            }
306    
307            /** set the value delimiter
308            *  Required for creating a query. Options are a tab or comma. Default is a comma.
309            * @param delimiter value to set
310            **/
311            public void setDelimiter(String delimiter)      {
312                    this.delimiter=delimiter.length()==0?',':delimiter.charAt(0);
313            }
314    
315            /** set the value resolveurl
316            *  Yes or No. Default is No. For GET and POST operations, if Yes, page reference returned into the 
317            *       fileContent internal variable has its internal URLs fully resolved, including port number, so that 
318            *       links remain intact.
319            * @param resolveurl value to set
320            **/
321            public void setResolveurl(boolean resolveurl)   {
322                    this.resolveurl=resolveurl;
323            }
324    
325            /** set the value timeout
326            * @param timeout value to set
327             * @throws ExpressionException 
328            **/
329            public void setTimeout(double timeout) throws ExpressionException       {
330                    if(timeout<0)
331                            throw new ExpressionException("invalid value ["+Caster.toString(timeout)+"] for attribute timeout, value must be a positive integer greater or equal than 0");
332                    
333                long requestTimeout = pageContext.getRequestTimeout();
334                long _timeout=(long)(timeout*1000D);
335                this.timeout=requestTimeout<_timeout?requestTimeout:_timeout;
336                    //print.out("this.timeout:"+this.timeout);
337            }
338    
339            /** set the value proxyserver
340            *  Host name or IP address of a proxy server.
341            * @param proxyserver value to set
342            **/
343            public void setProxyserver(String proxyserver)  {
344                    this.proxyserver=proxyserver;
345            }
346            
347            /** set the value proxyport
348            *  The port number on the proxy server from which the object is requested. Default is 80. When 
349            *       used with resolveURL, the URLs of retrieved documents that specify a port number are automatically 
350            *       resolved to preserve links in the retrieved document.
351            * @param proxyport value to set
352            **/
353            public void setProxyport(double proxyport)      {
354                    this.proxyport=(int)proxyport;
355            }
356    
357            /** set the value file
358            *  The filename to be used for the file that is accessed. For GET operations, defaults to the name 
359            *       pecified in url. Enter path information in the path attribute.
360            * @param file value to set
361            **/
362            public void setFile(String file)        {
363                    this.strFile=file;
364            }
365    
366            /** set the value throwonerror
367            *  Boolean indicating whether to throw an exception that can be caught by using the cftry and 
368            *       cfcatch tags. The default is NO.
369            * @param throwonerror value to set
370            **/
371            public void setThrowonerror(boolean throwonerror)       {
372                    this.throwonerror=throwonerror;
373            }
374    
375            /** set the value charset
376            *  set the charset for the call.
377            * @param charset value to set
378            **/
379            public void setCharset(String charset)  {
380                    this.charset=charset;
381            }
382    
383            /** set the value columns
384            * @param columns value to set
385             * @throws PageException
386            **/
387            public void setColumns(String columns) throws PageException     {
388                    this.columns=List.toStringArray(List.listToArrayRemoveEmpty(columns,","));
389            }
390    
391            /** set the value port
392            *  The port number on the server from which the object is requested. Default is 80. When used with 
393            *       resolveURL, the URLs of retrieved documents that specify a port number are automatically resolved to 
394            *       preserve links in the retrieved document. If a port number is specified in the url attribute, the port
395            *       value overrides the value of the port attribute.
396            * @param port value to set
397            **/
398            public void setPort(double port)        {
399                    this.port=(int) port;
400            }
401    
402            /** set the value useragent
403            *  User agent request header.
404            * @param useragent value to set
405            **/
406            public void setUseragent(String useragent)      {
407                    this.useragent=useragent;
408            }
409    
410            /** set the value textqualifier
411            *  Required for creating a query. Indicates the start and finish of a column. Should be 
412            *       appropriately escaped when embedded in a column. For example, if the qualifier is a double quotation 
413            *       mark, it should be escaped as """". If there is no text qualifier in the file, specify it as " ". 
414            *       Default is the double quotation mark (").
415            * @param textqualifier value to set
416            **/
417            public void setTextqualifier(String textqualifier)      {
418                    this.textqualifier=textqualifier.length()==0?'"':textqualifier.charAt(0);
419            }
420    
421            /** set the value username
422            *  When required by a proxy server, a valid username.
423            * @param proxyuser value to set
424            **/
425            public void setProxyuser(String proxyuser)      {
426                    this.proxyuser=proxyuser;
427            }
428    
429            /** set the value username
430            *  When required by a server, a valid username.
431            * @param username value to set
432            **/
433            public void setUsername(String username)        {
434                    this.username=username;
435            }
436    
437            /** set the value url
438            *  Full URL of the host name or IP address of the server on which the file resides. The URL must be
439            *       an absolute URL, including the protocol (http or https) and hostname. It may optionally contain a port
440            *       number. Port numbers specified in the url attribute override the port attribute.
441            * @param url value to set
442            **/
443            public void setUrl(String url)  {
444                    this.url=url;
445            }
446    
447            /** set the value redirect
448            * @param redirect value to set
449            **/
450            public void setRedirect(boolean redirect)       {
451                    this.redirect=redirect;
452            }
453    
454            /** set the value path
455            *  The path to the directory in which a file is to be stored. If a path is not specified in a POST 
456            *       or GET operation, a variable is created (cfhttp.fileContent) that you can use to display the results 
457            *       of the POST operation in a cfoutput.
458            * @param path value to set
459            **/
460            public void setPath(String path)        {
461                    this.strPath=path;
462            }
463    
464            /** set the value name
465            *  The name to assign to a query if the a query is constructed from a file.
466            * @param name value to set
467            **/
468            public void setName(String name)        {
469                    this.name=name;
470            }
471    
472            /** set the value method
473            *  GET or POST. Use GET to download a text or binary file or to create a query from the contents 
474            *       of a text file. Use POST to send information to a server page or a CGI program for processing. POST 
475            *       requires the use of a cfhttpparam tag.
476            * @param method value to set
477             * @throws ApplicationException
478            **/
479            public void setMethod(String method) throws ApplicationException        {
480                method=method.toLowerCase().trim();
481                if(method.equals("post")) this.method=METHOD_POST;
482                else if(method.equals("get")) this.method=METHOD_GET;
483                else if(method.equals("head")) this.method=METHOD_HEAD;
484                else if(method.equals("delete")) this.method=METHOD_DELETE;
485                else if(method.equals("put")) this.method=METHOD_PUT;
486                else if(method.equals("trace")) this.method=METHOD_TRACE;
487                else if(method.equals("options")) this.method=METHOD_OPTIONS;
488                else throw new ApplicationException("invalid method type ["+(method.toUpperCase())+"], valid types are POST,GET,HEAD,DELETE,PUT,TRACE,OPTIONS");
489            }
490    
491    
492            /**
493             * @see javax.servlet.jsp.tagext.Tag#doStartTag()
494             */
495            public int doStartTag() {
496                    if(addtoken) {
497                            setParam("cookie","cfid",pageContext.getCFID());
498                            setParam("cookie","cftoken",pageContext.getCFToken());
499                            String jsessionid = pageContext.getJSessionId();
500                            if(jsessionid!=null)setParam("cookie","jsessionid",jsessionid);
501                    }
502                    
503                    return EVAL_BODY_INCLUDE;
504            }
505    
506            private void setParam(String type, String name, String value) {
507                    HttpParamBean hpb = new HttpParamBean();
508                    hpb.setType(type);
509                    hpb.setName(name);
510                    hpb.setValue(value);
511                    setParam(hpb);
512            }
513    
514            /**
515             * @throws PageException
516             * @see javax.servlet.jsp.tagext.Tag#doEndTag()
517             */
518            public int doEndTag() throws PageException {
519                Struct cfhttp=new StructImpl();
520                    cfhttp.setEL(ERROR_DETAIL,"");
521                    pageContext.setVariable(result,cfhttp);
522    
523                    // because commons 
524                    PrintStream out = System.out;
525            try {
526                    //System.setOut(new PrintStream(DevNullOutputStream.DEV_NULL_OUTPUT_STREAM));
527                 _doEndTag(cfhttp);
528                 return EVAL_PAGE;
529            } 
530            catch (IOException e) {
531                throw Caster.toPageException(e);
532            }
533            finally {
534                    System.setOut(out);
535            }
536    
537            }
538    
539            
540            
541            private void _doEndTag(Struct cfhttp) throws PageException, IOException {
542                    HttpConnectionManager manager=new SimpleHttpConnectionManager();//MultiThreadedHttpConnectionManager();
543                    HttpClient client = new HttpClient(manager);
544                    HttpMethod httpMethod=createMethod(pageContext.getConfig(),this,client,url,port);
545                    try {
546                    
547    /////////////////////////////////////////// EXECUTE /////////////////////////////////////////////////
548                    Executor e = new Executor(this,client,httpMethod,redirect);
549                    if(timeout<0){
550                            try{
551                                    e.execute();
552                            }
553                            
554                            catch(Throwable t){
555                                    if(!throwonerror){
556                                            setUnknownHost(cfhttp, t);
557                                            return;
558                                    }
559                                    throw toPageException(t);
560                                    
561                            }
562                    }
563                    else {
564                            e.start();
565                            try {
566                                    synchronized(this){//print.err(timeout);
567                                            this.wait(timeout);
568                                    }
569                            } catch (InterruptedException ie) {
570                                    throw Caster.toPageException(ie);
571                            }
572                            if(e.t!=null){
573                                    if(!throwonerror){
574                                            setUnknownHost(cfhttp,e.t);
575                                            return;
576                                    }
577                                    throw toPageException(e.t);     
578                            }
579                            
580                            httpMethod=e.httpMethod;
581                            
582                            
583                            if(!e.done){
584                                    httpMethod.abort();
585                                    if(throwonerror)
586                                            throw new HTTPException("408 Request Time-out","a timeout occurred in tag http",408);
587                                    setRequestTimeout(cfhttp);      
588                                    return;
589                                    //throw new ApplicationException("timeout");    
590                            }
591                    }
592                    httpMethod=e.httpMethod;
593    /////////////////////////////////////////// EXECUTE /////////////////////////////////////////////////
594                    int status = httpMethod.getStatusCode();
595                    
596                    String responseCharset=charset;
597            // Write Response Scope
598                    //String rawHeader=httpMethod.getStatusLine().toString();
599                            String mimetype=null;
600                            String contentEncoding=null;
601                            
602                    // status code
603                            cfhttp.set(STATUSCODE,((httpMethod.getStatusCode()+" "+httpMethod.getStatusText()).trim()));
604                            cfhttp.set(STATUS_CODE,new Double(httpMethod.getStatusCode()));
605                            cfhttp.set(STATUS_TEXT,(httpMethod.getStatusText()));
606                            cfhttp.set(HTTP_VERSION,(httpMethod.getStatusLine().getHttpVersion()));
607                            
608                    //responseHeader
609                            Header[] headers = httpMethod.getResponseHeaders();
610                            StringBuffer raw=new StringBuffer(httpMethod.getStatusLine().toString()+" ");
611                            Struct responseHeader = new StructImpl();
612                            Array setCookie = new ArrayImpl();
613                            
614                    for(int i=0;i<headers.length;i++) {
615                            Header header=headers[i];
616                            //print.ln(header);
617                            
618                            raw.append(header+" ");
619                            if(header.getName().equalsIgnoreCase("Set-Cookie"))
620                                    setCookie.append(header.getValue());
621                            else {
622                                //print.ln(header.getName()+"-"+header.getValue());
623                                    Object value=responseHeader.get(KeyImpl.getInstance(header.getName()),null);
624                                    if(value==null) responseHeader.set(KeyImpl.getInstance(header.getName()),header.getValue());
625                                    else {
626                                        Array arr=null;
627                                        if(value instanceof Array) {
628                                            arr=(Array) value;
629                                        }
630                                        else {
631                                            arr=new ArrayImpl();
632                                            responseHeader.set(KeyImpl.getInstance(header.getName()),arr);
633                                            arr.appendEL(value);
634                                        }
635                                        arr.appendEL(header.getValue());
636                                    }
637                            }
638                            
639                            // Content-Type
640                            if(header.getName().equalsIgnoreCase("Content-Type")) {
641                                    mimetype=header.getValue();
642                                if(mimetype==null)mimetype=NO_MIMETYPE;
643                            }
644                            
645                            // Content-Encoding
646                            if(header.getName().equalsIgnoreCase("Content-Encoding")) {
647                                    contentEncoding=header.getValue();
648                            }
649                            
650                    }
651                    cfhttp.set(RESPONSEHEADER,responseHeader);
652                    responseHeader.set(STATUS_CODE,new Double(httpMethod.getStatusCode()));
653                    responseHeader.set(EXPLANATION,(httpMethod.getStatusText()));
654                    if(setCookie.size()>0)responseHeader.set(SET_COOKIE,setCookie);
655                    
656                // is text 
657                    boolean isText=
658                            mimetype == null ||  
659                            mimetype == NO_MIMETYPE || isText(mimetype);
660                            
661                    
662                   
663                    cfhttp.set(TEXT,Caster.toBoolean(isText));
664                    
665                // mimetype charset
666                    //boolean responseProvideCharset=false;
667                    if(!StringUtil.isEmpty(mimetype)){
668                            if(isText) {
669                                    String[] types=HTTPUtil.splitMimeTypeAndCharset(mimetype);
670                                    if(types[0]!=null)cfhttp.set(MIME_TYPE,types[0]);
671                                    if(types[1]!=null)cfhttp.set(CHARSET,types[1]);
672                            
673                            }
674                            else cfhttp.set(MIME_TYPE,mimetype);
675                    }
676                    else cfhttp.set(MIME_TYPE,NO_MIMETYPE);
677    
678                // File
679                    Resource file=null;
680                    
681                    if(strFile!=null && strPath!=null) {
682                        file=ResourceUtil.toResourceNotExisting(pageContext, strPath).getRealResource(strFile);
683                    }
684                    else if(strFile!=null) {
685                        file=ResourceUtil.toResourceNotExisting(pageContext, strFile);
686                    }
687                    else if(strPath!=null) {
688                        file=ResourceUtil.toResourceNotExisting(pageContext, strPath);
689                        //Resource dir = file.getParentResource();
690                        if(file.isDirectory()){
691                            file=file.getRealResource(httpMethod.getURI().getName());
692                        }
693                        
694                    }
695                    if(file!=null)pageContext.getConfig().getSecurityManager().checkFileLocation(file);
696                    
697                    
698                    // filecontent
699                    //try {
700                    //print.ln(">> "+responseCharset);
701    
702                        InputStream is=null;
703                        if(isText && getAsBinary!=GET_AS_BINARY_YES) {
704                            String str;
705                    try {
706                            is = httpMethod.getResponseBodyAsStream();
707                        if(is!=null &&isGzipEncoded(contentEncoding))
708                            is = new GZIPInputStream(is);
709                                    
710                        try {
711                            str = is==null?"":IOUtil.toString(is,responseCharset);
712                        }
713                        catch (UnsupportedEncodingException uee) {
714                            str = is==null?"":IOUtil.toString(is,null);
715                        }
716                    }
717                    catch (IOException ioe) {
718                            throw Caster.toPageException(ioe);
719                    }
720                    finally {
721                            IOUtil.closeEL(is);
722                    }
723                        
724                    if(str==null)str="";
725                            if(resolveurl){
726                                    //URI uri = httpMethod.getURI();
727                                    if(e!=null && e.redirectURL!=null)url=e.redirectURL.toExternalForm();
728                                    str=new URLResolver().transform(str,new URL(url),false);
729                            }
730                            cfhttp.set(FILE_CONTENT,str);
731                            try {
732                                    if(file!=null){
733                                            IOUtil.write(file,str,pageContext.getConfig().getWebCharset(),false);
734                        }
735                    } 
736                            catch (IOException e1) {}
737                            
738                            if(name!=null) {
739                                    Query qry = new CSVParser().parse(str,delimiter,textqualifier,columns,firstrowasheaders);
740                        pageContext.setVariable(name,qry);
741                            }
742                        }
743                        // Binary
744                        else {
745                            byte[] barr=null;
746                            if(isGzipEncoded(contentEncoding)){
747                                    is = new GZIPInputStream(httpMethod.getResponseBodyAsStream());
748                                    try {
749                                            barr = IOUtil.toBytes(is);
750                                            } 
751                                    catch (IOException t) {
752                                            throw Caster.toPageException(t);
753                                            }
754                                            finally{
755                                                    IOUtil.closeEL(is);
756                                            }
757                            }
758                            else {
759                                    try {
760                                            barr = httpMethod.getResponseBody();
761                                            } 
762                                    catch (IOException t) {
763                                            throw Caster.toPageException(t);
764                                            }
765                            }
766                                    
767                            cfhttp.set(FILE_CONTENT,barr);
768                            
769                            if(file!=null) {
770                                    try {
771                                            if(barr!=null)IOUtil.copy(new ByteArrayInputStream(barr),file,true);
772                                    } 
773                                    catch (IOException ioe) {
774                                    throw Caster.toPageException(ioe);
775                                    }
776                            }   
777                        }
778                    
779                // header           
780                    cfhttp.set(HEADER,raw.toString());
781                   
782                    if(status!=STATUS_OK){
783                        cfhttp.setEL(ERROR_DETAIL,httpMethod.getStatusCode()+" "+httpMethod.getStatusText());
784                        if(throwonerror)throw new HTTPException(httpMethod);
785                    }
786                    }
787                    finally {
788                            releaseConnection(httpMethod);
789                    }
790                
791            }
792    
793            public static boolean isText(String mimetype) {
794                    if(mimetype==null)mimetype="";
795                    else mimetype=mimetype.trim().toLowerCase();
796                    return StringUtil.startsWithIgnoreCase(mimetype,"text")  || 
797            StringUtil.startsWithIgnoreCase(mimetype,"application/xml")  || 
798            StringUtil.startsWithIgnoreCase(mimetype,"application/atom+xml")  || 
799            StringUtil.startsWithIgnoreCase(mimetype,"application/xhtml")  || 
800            StringUtil.startsWithIgnoreCase(mimetype,"message") || 
801            StringUtil.startsWithIgnoreCase(mimetype,"application/octet-stream") || 
802            StringUtil.indexOfIgnoreCase(mimetype, "xml")!=-1 || 
803            StringUtil.indexOfIgnoreCase(mimetype, "json")!=-1 || 
804            StringUtil.indexOfIgnoreCase(mimetype, "rss")!=-1 || 
805            StringUtil.indexOfIgnoreCase(mimetype, "atom")!=-1 || 
806            StringUtil.indexOfIgnoreCase(mimetype, "text")!=-1;
807                    
808                    // "application/x-www-form-urlencoded" ???
809            }
810    
811            private PageException toPageException(Throwable t) {
812                    PageException pe = Caster.toPageException(t);
813                    if(pe instanceof NativeException) {
814                            ((NativeException) pe).setAdditional("url", url);
815                    }
816                    return pe;
817            }
818    
819            private void setUnknownHost(Struct cfhttp,Throwable t) {
820                    cfhttp.setEL(CHARSET,"");
821                    cfhttp.setEL(ERROR_DETAIL,"Unknown host: "+t.getMessage());
822                    cfhttp.setEL(FILE_CONTENT,"Connection Failure");
823                    cfhttp.setEL(HEADER,"");
824                    cfhttp.setEL(MIME_TYPE,"Unable to determine MIME type of file.");
825                    cfhttp.setEL(RESPONSEHEADER,new StructImpl());
826                    cfhttp.setEL(STATUSCODE,"Connection Failure. Status code unavailable.");
827                    cfhttp.setEL(TEXT,Boolean.TRUE);
828            }
829    
830            private void setRequestTimeout(Struct cfhttp) {
831                    cfhttp.setEL(CHARSET,"");
832                    cfhttp.setEL(ERROR_DETAIL,"");
833                    cfhttp.setEL(FILE_CONTENT,"Connection Timeout");
834                    cfhttp.setEL(HEADER,"");
835                    cfhttp.setEL(MIME_TYPE,"Unable to determine MIME type of file.");
836                    cfhttp.setEL(RESPONSEHEADER,new StructImpl());
837                    cfhttp.setEL(STATUSCODE,"408 Request Time-out");
838                    cfhttp.setEL(STATUS_CODE,new Double(408));
839                    cfhttp.setEL(STATUS_TEXT,"Request Time-out");
840                    cfhttp.setEL(TEXT,Boolean.TRUE);
841            }
842    
843            /*private static HttpMethod execute(Http http, HttpClient client, HttpMethod httpMethod, boolean redirect) throws PageException {
844                    try {
845                            // Execute Request
846                            short count=0;
847                    URL lu;
848                    
849                    while(isRedirect(client.executeMethod(httpMethod)) && redirect && count++ < MAX_REDIRECT) { 
850                            lu=locationURL(httpMethod);
851                            httpMethod=createMethod(http,client,lu.toExternalForm(),-1);
852                    }
853            } 
854                    catch (IOException e) {
855                    PageException pe = Caster.toPageException(e);
856                            if(pe instanceof NativeException) {
857                                    ((NativeException) pe).setAdditional("url", HTTPUtil.toURL(httpMethod));
858                            }
859                            throw pe;
860            }
861                    return httpMethod;
862            }*/
863    
864            public static void releaseConnection(HttpMethod httpMethod) {
865                    httpMethod.releaseConnection();
866                    //manager.closeIdleConnections(0);
867            }
868    
869            static URL locationURL(HttpMethod method) throws MalformedURLException, ExpressionException {
870            Header location = method.getResponseHeader("location");
871            
872            if(location==null) throw new ExpressionException("missing location header definition");
873            
874            
875            HostConfiguration config = method.getHostConfiguration();
876            URL url;
877            try {
878                url = new URL(location.getValue());
879            } 
880            catch (MalformedURLException e) {
881                url=new URL(config.getProtocol().getScheme(),
882                        config.getHost(),
883                        config.getPort(),
884                        mergePath(method.getPath(),location.getValue()));
885            }
886                
887            return url;
888        }
889            
890    
891            static HttpMethod createMethod(Config cw,Http http, HttpClient client, String url, int port) throws PageException, UnsupportedEncodingException {
892                    String _charset=http.charset;
893                    if(StringUtil.isEmpty(_charset,true)) _charset=cw.getWebCharset();
894                    else _charset=_charset.trim();
895                    
896                    
897                    HttpMethod httpMethod;
898                    HostConfiguration config = client.getHostConfiguration();
899                    HttpState state = client.getState();
900                    
901                    String[] arrQS=new String[0];
902            // check if has fileUploads     
903                    boolean doUploadFile=false;
904                    for(int i=0;i<http.params.size();i++) {
905                            if(((HttpParamBean)http.params.get(i)).getType().equals("file")) {
906                                    doUploadFile=true;
907                                    break;
908                            }
909                    }       
910            
911            // parse url (also query string)
912                    URL _url=null;
913                    try {
914                            _url = HTTPUtil.toURL(url,port);
915                            url=_url.toExternalForm();
916                            
917                            
918                    } catch (MalformedURLException mue) {
919                            throw Caster.toPageException(mue);
920                    }
921                    
922                    
923            // QS
924                    String strQS=_url.getQuery();
925                    if(strQS!=null) {
926                            arrQS=List.toStringArray(List.listToArray(List.trim(strQS,"&"),"&"));
927                    }
928                    
929            // select best matching method (get,post, post multpart (file))
930    
931                    boolean isBinary = false;
932                    boolean doMultiPart=doUploadFile || http.multiPart;
933                    PostMethod post=null;
934                    EntityEnclosingMethod eem=null;
935                    
936                    
937                    if(http.method==METHOD_GET) {
938                            httpMethod=new GetMethod(url);
939                    }
940                    else if(http.method==METHOD_HEAD) {
941                        httpMethod=new HeadMethod(url);
942                    }
943                    else if(http.method==METHOD_DELETE) {
944                            isBinary=true;
945                        httpMethod=new DeleteMethod(url);
946                    }
947                    else if(http.method==METHOD_PUT) {
948                            isBinary=true;
949                        httpMethod=eem=new PutMethod(url);
950                        
951                    }
952                    else if(http.method==METHOD_TRACE) {
953                            isBinary=true;
954                        httpMethod=new TraceMethod(url);
955                    }
956                    else if(http.method==METHOD_OPTIONS) {
957                            isBinary=true;
958                        httpMethod=new OptionsMethod(url);
959                    }
960                    else {
961                            isBinary=true;
962                            post=new PostMethod(url);
963                            httpMethod=eem=post;
964                    }
965                    // content type
966                    if(StringUtil.isEmpty(http.charset))http.charset=http.pageContext.getConfig().getWebCharset();
967                    
968            
969                    boolean hasForm=false;
970                    boolean hasBody=false;
971                    boolean hasContentType=false;
972            // Set http params
973                    ArrayList<NameValuePair> listQS=new ArrayList<NameValuePair>();
974                    ArrayList<Part> parts=new ArrayList<Part>();
975                    int len=http.params.size();
976                    StringBuilder acceptEncoding=new StringBuilder();
977                    for(int i=0;i<len;i++) {
978                            HttpParamBean param=(HttpParamBean)http.params.get(i);
979                            String type=param.getType();
980                    // URL
981                            if(type.equals("url")) {
982                                    listQS.add(new NameValuePair(translateEncoding(param.getName(), http.charset),translateEncoding(param.getValueAsString(), http.charset)));
983                            }
984                    // Form
985                            else if(type.equals("formfield") || type.equals("form")) {
986                                    hasForm=true;
987                                    if(http.method==METHOD_GET) throw new ApplicationException("httpparam type formfield can't only be used, when method of the tag http equal post");
988                                    if(post!=null){
989                                            if(doMultiPart){
990                                                    parts.add(new RailoStringPart(param.getName(),param.getValueAsString(),_charset));
991                                            }
992                                            else post.addParameter(new NameValuePair(param.getName(),param.getValueAsString()));
993                                    }
994                                    //else if(multi!=null)multi.addParameter(param.getName(),param.getValueAsString());
995                            }
996                    // CGI
997                            else if(type.equals("cgi")) {
998                                    if(param.isEncoded())
999                                        httpMethod.addRequestHeader(
1000                                translateEncoding(param.getName(),http.charset),
1001                                translateEncoding(param.getValueAsString(),http.charset));
1002                    else
1003                        httpMethod.addRequestHeader(param.getName(),param.getValueAsString());
1004                            }
1005            // Header
1006                else if(type.startsWith("head")) {
1007                    if(param.getName().equalsIgnoreCase("content-type")) hasContentType=true;
1008                    
1009                    if(param.getName().equalsIgnoreCase("Accept-Encoding")) {
1010                            acceptEncoding.append(headerValue(param.getValueAsString()));
1011                            acceptEncoding.append(", ");
1012                    }
1013                    else httpMethod.addRequestHeader(param.getName(),headerValue(param.getValueAsString()));
1014                }
1015                    // Cookie
1016                            else if(type.equals("cookie")) {
1017                                    Cookie c=CookieUtil.toCookie(_url.getHost(),param.getName(),param.getValueAsString(),_charset);
1018                                    c.setPath("/");
1019                                    client.getState().addCookie(c);
1020                            }
1021                    // File
1022                            else if(type.equals("file")) {
1023                                    hasForm=true;
1024                                    if(http.method==METHOD_GET) throw new ApplicationException("httpparam type file can't only be used, when method of the tag http equal post");
1025                                    if(doMultiPart) {
1026                                            try {
1027                                                    parts.add(new ResourcePart(param.getName(),new ResourcePartSource(param.getFile()),getContentType(param),_charset));
1028                                            } 
1029                                            catch (FileNotFoundException e) {
1030                                                    throw new ApplicationException("can't upload file, path is invalid",e.getMessage());
1031                                            }
1032                                    }
1033                            }
1034                    // XML
1035                            else if(type.equals("xml")) {
1036                                    hasBody=true;
1037                                    hasContentType=true;
1038                                    httpMethod.addRequestHeader("Content-type", "text/xml; charset="+http.charset);
1039                                //post.setRequestBody(new NameValuePair [] {new NameValuePair(translateEncoding(param.getName(), charset),translateEncoding(param.getValue(), charset))});
1040                                    if(eem==null)throw new ApplicationException("type xml is only supported for type post and put");
1041                                eem.setRequestBody(param.getValueAsString());
1042                            }
1043                    // Body
1044                            else if(type.equals("body")) {
1045                                    hasBody=true;
1046                                    if(eem==null)throw new ApplicationException("type body is only supported for type post and put");
1047                                Object value = param.getValue();
1048                                
1049                                if(value instanceof InputStream) {
1050                                            eem.setRequestEntity(new InputStreamRequestEntity((InputStream)value,"application/octet-stream"));
1051                                    }
1052                                    else if(Decision.isCastableToBinary(value,false)){
1053                                            eem.setRequestEntity(new ByteArrayRequestEntity(Caster.toBinary(value)));
1054                                    }
1055                                    else {
1056                                            eem.setRequestEntity(new StringRequestEntity(param.getValueAsString()));
1057                                    }
1058                            }
1059                else {
1060                    throw new ApplicationException("invalid type ["+type+"]");
1061                }
1062                        
1063                    }
1064                    
1065                    httpMethod.setRequestHeader("Accept-Encoding",acceptEncoding.append("gzip").toString());
1066                    
1067                    
1068                    
1069                    // multipart
1070                    if(doMultiPart && eem!=null) {
1071                            hasContentType=true;
1072                            boolean doIt=true;
1073                            if(!http.multiPart && parts.size()==1){
1074                                    Part part = parts.get(0);
1075                                    /* jira 1513
1076                                      if(part instanceof ResourcePart){
1077                                            ResourcePart rp = (ResourcePart) part;
1078                                            eem.setRequestEntity(new ResourceRequestEntity(rp.getResource(),rp.getContentType()));
1079                                            doIt=false;
1080                                    }
1081                                    else */
1082                                            if(part instanceof RailoStringPart){
1083                                            RailoStringPart sp = (RailoStringPart) part;
1084                                            try {
1085                                                    eem.setRequestEntity(new StringRequestEntity(sp.getValue(),sp.getContentType(),sp.getCharSet()));
1086                                            } catch (IOException e) {
1087                                                    throw Caster.toPageException(e);
1088                                            }
1089                                            doIt=false;
1090                                    }
1091                            }
1092                            if(doIt)
1093                                    eem.setRequestEntity(new MultipartRequestEntityFlex(parts.toArray(new Part[parts.size()]), eem.getParams(),http.multiPartType));
1094                    }
1095                    
1096                    
1097                    
1098                    if(hasBody && hasForm)
1099                            throw new ApplicationException("mixing httpparam  type file/formfield and body/XML is not allowed");
1100            
1101                    if(!hasContentType) {
1102                            if(isBinary) {
1103                                    if(hasBody) httpMethod.addRequestHeader("Content-type", "application/octet-stream");
1104                                    else httpMethod.addRequestHeader("Content-type", "application/x-www-form-urlencoded; charset="+http.charset);
1105                            }
1106                            else {
1107                                    if(hasBody)
1108                                            httpMethod.addRequestHeader("Content-type", "text/html; charset="+http.charset ); 
1109                            }
1110                    }
1111                    
1112                    
1113                    // set User Agent
1114                            httpMethod.setRequestHeader("User-Agent",http.useragent);
1115                    
1116            // set timeout
1117                    if(http.timeout>0L)client.setConnectionTimeout((int)http.timeout);
1118                    // for 3.0 client.getParams().setConnectionManagerTimeout(timeout);
1119                    
1120            // set Query String
1121                    //NameValuePair[] qsPairs=new NameValuePair[arrQS.length+listQS.size()];
1122                    java.util.List<NameValuePair> listPairs=new ArrayList<NameValuePair>();
1123                    
1124                    //int count=0;
1125                    // QS from URL
1126                    for(int i=0;i<arrQS.length;i++) {
1127                            if(StringUtil.isEmpty(arrQS[i])) continue;
1128                            
1129                            String[] pair=List.toStringArray(List.listToArray(arrQS[i],'='));
1130                            if(ArrayUtil.isEmpty(pair)) continue;
1131                            
1132                            String name=pair[0];
1133                            String value=pair.length>1?pair[1]:null;
1134                            listPairs.add(new NameValuePair(name,value));
1135                    }
1136                    
1137                    // QS from http Param
1138                    len=listQS.size();
1139                    for(int i=0;i<len;i++) {
1140                            listPairs.add(listQS.get(i));
1141                    }
1142                    
1143                    // set to method
1144                    String qs = toQueryString((NameValuePair[]) listPairs.toArray(new NameValuePair[listPairs.size()]));
1145                    if(!StringUtil.isEmpty(qs))
1146                            httpMethod.setQueryString(qs);
1147    
1148                    
1149            // set Username and Password
1150                    if(http.username!=null) {
1151                            if(http.password==null)http.password="";
1152                            //client.getState().setAuthenticationPreemptive(true);
1153                            client.getState().setCredentials(null,null,new UsernamePasswordCredentials(http.username, http.password));
1154                            httpMethod.setDoAuthentication( true );
1155                            client.getState().setAuthenticationPreemptive(true);
1156                            
1157                    }
1158            
1159            // set Proxy
1160                    if(StringUtil.isEmpty(http.proxyserver) && http.pageContext.getConfig().isProxyEnableFor(_url.getHost())) { 
1161                            http.proxyserver=http.pageContext.getConfig().getProxyServer();
1162                            http.proxyport=http.pageContext.getConfig().getProxyPort();
1163                            http.proxyuser=http.pageContext.getConfig().getProxyUsername();
1164                            http.proxypassword=http.pageContext.getConfig().getProxyPassword();
1165                    }
1166                    if(!StringUtil.isEmpty(http.proxyserver)) {
1167                config.setProxy(http.proxyserver,http.proxyport);
1168                if(!StringUtil.isEmpty(http.proxyuser)) {
1169                    state.setProxyCredentials(null,null,new UsernamePasswordCredentials(http.proxyuser,http.proxypassword));
1170                }
1171            }
1172    
1173                    
1174    
1175                    httpMethod.setFollowRedirects(false);
1176                return httpMethod;
1177            }
1178    
1179            private static String headerValue(String value) {
1180                    if(value==null) return null;
1181                    value=value.trim();
1182                    int len=value.length();
1183                    char c;
1184                    for(int i=0;i<len;i++){
1185                            c=value.charAt(i);
1186                            if(c=='\n' || c=='\r') return value.substring(0,i);
1187                    }
1188                    return value;
1189            }
1190    
1191            private static String toQueryString(NameValuePair[] qsPairs) {
1192                    StringBuffer sb=new StringBuffer();
1193            for(int i=0;i<qsPairs.length;i++) {
1194                if(sb.length()>0)sb.append('&');
1195                sb.append(qsPairs[i].getName());
1196                if(qsPairs[i].getValue()!=null){
1197                    sb.append('=');
1198                    sb.append(qsPairs[i].getValue());
1199                }
1200            }
1201            return sb.toString();
1202        }
1203    
1204        private static String translateEncoding(String str, String charset) throws UnsupportedEncodingException {
1205            if(!ReqRspUtil.needEncoding(str,false)) return str;
1206            return URLEncoder.encode(str,charset);
1207        }
1208    
1209        /**
1210            * @see javax.servlet.jsp.tagext.BodyTag#doInitBody()
1211            */
1212            public void doInitBody()        {
1213                    
1214            }
1215    
1216            /**
1217            * @see javax.servlet.jsp.tagext.BodyTag#doAfterBody()
1218            */
1219            public int doAfterBody()        {
1220                    return SKIP_BODY;
1221            }
1222    
1223            /**
1224             * sets if has body or not
1225             * @param hasBody
1226             */
1227            public void hasBody(boolean hasBody) {
1228                
1229            }
1230    
1231            /**
1232             * @param param
1233             */
1234            public void setParam(HttpParamBean param) {
1235                    params.add(param);
1236                    
1237            }
1238            
1239            
1240        /**
1241         * @param getAsBinary The getasbinary to set.
1242         */
1243        public void setGetasbinary(String getAsBinary) {
1244            // TODO support never, wird das verwendet?
1245            getAsBinary=getAsBinary.toLowerCase().trim();
1246            if(getAsBinary.equals("yes") || getAsBinary.equals("true"))             this.getAsBinary=GET_AS_BINARY_YES;
1247            else if(getAsBinary.equals("no") || getAsBinary.equals("false"))        this.getAsBinary=GET_AS_BINARY_NO;
1248            else if(getAsBinary.equals("auto"))                                                             this.getAsBinary=GET_AS_BINARY_AUTO;
1249        }
1250    
1251        /**
1252         * @param multipart The multipart to set.
1253         */
1254        public void setMultipart(boolean multiPart) {
1255            this.multiPart = multiPart;
1256        }
1257    
1258        /**
1259         * @param multipart The multipart to set.
1260         * @throws ApplicationException 
1261         */
1262        public void setMultiparttype(String multiPartType) throws ApplicationException {
1263            if(StringUtil.isEmpty(multiPartType))return;
1264            multiPartType=multiPartType.trim().toLowerCase();
1265            
1266            if("form-data".equals(multiPartType))   this.multiPartType=MultipartRequestEntityFlex.MULTIPART_FORM_DATA;
1267            //else if("related".equals(multiPartType))              this.multiPartType=MultipartRequestEntityFlex.MULTIPART_RELATED;
1268            else
1269                            throw new ApplicationException("invalid value for attribute multiPartType ["+multiPartType+"]",
1270                                            "attribute must have one of the folloing values [form-data]");
1271                            
1272        }
1273    
1274        /**
1275         * @param result The result to set.
1276         */
1277        public void setResult(String result) {
1278            this.result = result;
1279        }
1280    
1281            /**
1282             * @param addtoken the addtoken to set
1283             */
1284            public void setAddtoken(boolean addtoken) {
1285                    this.addtoken = addtoken;
1286            }
1287            
1288            /**
1289         * checks if status code is a redirect
1290         * @param status
1291         * @return is redirect
1292         */
1293        
1294            static boolean isRedirect(int status) {
1295            return 
1296                    status==STATUS_REDIRECT_FOUND || 
1297                    status==STATUS_REDIRECT_MOVED_PERMANENTLY ||
1298                    status==STATUS_REDIRECT_SEE_OTHER ||
1299                    status==STATUS_REDIRECT_TEMPORARY_REDIRECT;
1300            
1301            
1302        }
1303        
1304        /**
1305         * merge to pathes to one
1306         * @param current
1307         * @param realPath
1308         * @return
1309         * @throws MalformedURLException
1310         */
1311        public static String mergePath(String current, String realPath) throws MalformedURLException {
1312            
1313            // get current directory
1314            String currDir;
1315            if(current==null || current.indexOf('/')==-1)currDir="/";
1316            else if(current.endsWith("/"))currDir=current;
1317            else currDir=current.substring(0,current.lastIndexOf('/')+1);
1318            
1319            // merge together
1320            String path;
1321            if(realPath.startsWith("./"))path=currDir+realPath.substring(2);
1322            else if(realPath.startsWith("/"))path=realPath;
1323            else if(!realPath.startsWith("../"))path=currDir+realPath;
1324            else {
1325                while(realPath.startsWith("../") || currDir.length()==0) {
1326                    realPath=realPath.substring(3);
1327                    currDir=currDir.substring(0,currDir.length()-1);
1328                    int index = currDir.lastIndexOf('/');
1329                    if(index==-1)throw new MalformedURLException("invalid realpath definition for URL");
1330                    currDir=currDir.substring(0,index+1);
1331                }
1332                path=currDir+realPath;
1333            }
1334            
1335            return path;
1336        }
1337        
1338            private static String getContentType(HttpParamBean param) {
1339                    String mimeType=param.getMimeType();
1340                    if(StringUtil.isEmpty(mimeType,true)) {
1341                            mimeType=ResourceUtil.getMymeType(param.getFile(), true, null);
1342                    }
1343                    return mimeType;
1344            }
1345    
1346            public static boolean isGzipEncoded(String contentEncoding) {
1347                    return !StringUtil.isEmpty(contentEncoding) && StringUtil.indexOfIgnoreCase(contentEncoding, "gzip")!=-1;
1348            }
1349    
1350            public static Object getOutput(InputStream is, String contentType, String contentEncoding, boolean closeIS) {
1351                    if(StringUtil.isEmpty(contentType))contentType="text/html";
1352                    
1353                    // Gzip
1354                    if(Http.isGzipEncoded(contentEncoding)){
1355                            try {
1356                                    is=new GZIPInputStream(is);
1357                            } 
1358                            catch (IOException e) {}
1359                    }
1360                    
1361                    try {
1362                            // text
1363                            if(isText(contentType)) {
1364                                    String[] tmp = HTTPUtil.splitMimeTypeAndCharset(contentType);
1365                                    //String mimetype=tmp[0];
1366                                    String charset=tmp[1];
1367                                    
1368                                    if(StringUtil.isEmpty(charset,true)) {
1369                                            Config config = ThreadLocalPageContext.getConfig();
1370                                            if(config!=null)charset=config.getWebCharset();
1371                                    }
1372                                    
1373                                    try {
1374                                            return IOUtil.toString(is, charset);
1375                                    } catch (IOException e) {}
1376                            }
1377                            // Binary
1378                            else {
1379                                    try {
1380                                            return IOUtil.toBytes(is);
1381                                    } 
1382                                    catch (IOException e) {}
1383                            }
1384                    }
1385                    finally{
1386                            if(closeIS)IOUtil.closeEL(is);
1387                    }
1388    
1389                    return "";
1390            }
1391            
1392    }
1393    
1394    class MultipartRequestEntityFlex extends MultipartRequestEntity {
1395            
1396            
1397            public static final String MULTIPART_RELATED = "multipart/related";
1398            public static final String MULTIPART_FORM_DATA = "multipart/form-data";
1399            private String multipartType;
1400            
1401            
1402            /**
1403             * Constructor of the class
1404             * @param parts
1405             * @param params
1406             * @param multipartType use constant MultipartRequestEntityFlex.MULTIPART_FORM_DATA or MultipartRequestEntityFlex.MULTIPART_RELATED
1407             */
1408            public MultipartRequestEntityFlex(Part[] parts, HttpMethodParams params,String multipartType) {
1409                    super(parts, params);
1410                    this.multipartType=multipartType;
1411            }
1412            
1413            /**
1414             * @see org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity#getContentType()
1415             */
1416            public String getContentType() {
1417               StringBuilder builder = new StringBuilder(multipartType);
1418               builder.append("; boundary=");
1419               builder.append(EncodingUtil.getAsciiString(getMultipartBoundary()));
1420    
1421               return builder.toString();
1422               
1423               //return super.getContentType();
1424            }
1425    }
1426    
1427    class Executor extends Thread {
1428            
1429             final Http http;
1430             final HttpClient client;
1431             HttpMethod httpMethod;
1432             final boolean redirect;
1433             Throwable t;
1434             boolean done;
1435            URL redirectURL;
1436    
1437            public Executor(Http http, HttpClient client,HttpMethod httpMethod,boolean redirect) {
1438                    this.http=http;
1439                    this.client=client;
1440                    this.httpMethod=httpMethod;
1441                    this.redirect=redirect;
1442            }
1443            
1444    
1445            public void run(){
1446                    try {
1447                            execute();
1448                            done=true;
1449                            synchronized(http){
1450                                    http.notify();
1451                            }
1452                    } catch (Throwable t) {
1453                            this.t=t;
1454                    }
1455            }
1456            
1457            public void execute() throws IOException, PageException {
1458                    // Execute Request 
1459                    
1460                    short count=0;
1461            URL lu;
1462            while(Http.isRedirect(client.executeMethod(httpMethod)) && redirect && count++ < Http.MAX_REDIRECT) { 
1463                    lu=Http.locationURL(httpMethod);
1464                    redirectURL=lu;
1465                    HttpMethod oldHttpMethod = httpMethod;
1466                    httpMethod=Http.createMethod(ThreadLocalPageContext.getConfig(),http,client,lu.toExternalForm(),-1);
1467                    Http.releaseConnection(oldHttpMethod);
1468            }
1469            
1470            }
1471    }