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