Package railo.runtime.tag

Source Code of railo.runtime.tag.Range

package railo.runtime.tag;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.zip.GZIPOutputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import railo.commons.io.IOUtil;
import railo.commons.io.res.Resource;
import railo.commons.io.res.util.ResourceUtil;
import railo.commons.lang.StringUtil;
import railo.commons.lang.SystemOut;
import railo.commons.net.HTTPUtil;
import railo.runtime.PageContextImpl;
import railo.runtime.exp.ApplicationException;
import railo.runtime.exp.PageException;
import railo.runtime.exp.PostContentAbort;
import railo.runtime.exp.TemplateException;
import railo.runtime.ext.tag.BodyTagImpl;
import railo.runtime.net.http.ReqRspUtil;
import railo.runtime.op.Caster;
import railo.runtime.type.util.ListUtil;

/**
* Defines the MIME type returned by the current page. Optionally, lets you specify the name of a file
*   to be returned with the page.
**/
public final class Content extends BodyTagImpl {

  private static final int RANGE_NONE = 0;
  private static final int RANGE_YES = 1;
  private static final int RANGE_NO = 2;

  /** Defines the File/ MIME content type returned by the current page. */
  private String type;

  /** The name of the file being retrieved */
  private String strFile;

  /** Yes or No. Yes discards output that precedes the call to cfcontent. No preserves the output that precedes the call. Defaults to Yes. The reset
  **     and file attributes are mutually exclusive. If you specify a file, the reset attribute has no effect. */
  private boolean reset=true;
 
  private int _range=RANGE_NONE;

  /** Yes or No. Yes deletes the file after the download operation. Defaults to No.
  **   This attribute applies only if you specify a file with the file attribute. */
  private boolean deletefile=false;

    private byte[] content;


    @Override
    public void release()   {
        super.release();
        type=null;
        strFile=null;
        reset=true;
        deletefile=false;
        content=null;
        _range=RANGE_NONE;
    }

  /** set the value type
  *  Defines the File/ MIME content type returned by the current page.
  * @param type value to set
  **/
  public void setType(String type)  {
    this.type=type.trim();
  }
 
  public void setRange(boolean range)  {
    this._range=range?RANGE_YES:RANGE_NO;
  }

    /** set the value file
    *  The name of the file being retrieved
    * @param file value to set
    **/
    public void setFile(String file)    {
        this.strFile=file;
    }

    /**
    * the content to output as binary
    * @param content value to set
    * @deprecated replaced with <code>{@link #setVariable(Object)}</code>
    **/
    public void setContent(byte[] content)    {
        this.content=content;
    }
   
    public void setVariable(Object variable) throws PageException    {
      if(variable instanceof String)
        this.content=Caster.toBinary(pageContext.getVariable((String)variable));
      else
        this.content=Caster.toBinary(variable);
    }

  /** set the value reset
  *  Yes or No. Yes discards output that precedes the call to cfcontent. No preserves the output that precedes the call. Defaults to Yes. The reset
  *     and file attributes are mutually exclusive. If you specify a file, the reset attribute has no effect.
  * @param reset value to set
  **/
  public void setReset(boolean reset)  {
    this.reset=reset;
  }

  /** set the value deletefile
  *  Yes or No. Yes deletes the file after the download operation. Defaults to No.
  *   This attribute applies only if you specify a file with the file attribute.
  * @param deletefile value to set
  **/
  public void setDeletefile(boolean deletefile)  {
    this.deletefile=deletefile;
  }


  @Override
    public int doStartTag() throws PageException   {
        //try {
            return _doStartTag();
        /*}
        catch (IOException e) {
            throw Caster.toPageException(e);
        }*/
    }


    private int _doStartTag() throws PageException   {
        // check the file before doing anyrhing else
      Resource file=null;
    if(content==null && !StringUtil.isEmpty(strFile))
        file = ResourceUtil.toResourceExisting(pageContext,strFile);

    // get response object
    HttpServletResponse rsp = pageContext. getHttpServletResponse();
     
        // check committed
        if(rsp.isCommitted())
            throw new ApplicationException("content is already flushed","you can't rewrite head of response after part of the page is flushed");
       
        // set type
        if(!StringUtil.isEmpty(type,true)) {
          type=type.trim();
          rsp.setContentType(type);
         
          // TODO more dynamic implementation, configuration in admin?
          if(!HTTPUtil.isTextMimeType(type)) {
            ((PageContextImpl)pageContext).getRootOut().setAllowCompression(false);
          }
        }
       
        Range[] ranges=getRanges();
        boolean hasRanges=ranges!=null && ranges.length>0;
        if(_range==RANGE_YES || hasRanges){
            rsp.setHeader("Accept-Ranges", "bytes");
        }
        else if(_range==RANGE_NO) {
            rsp.setHeader("Accept-Ranges", "none");
            hasRanges=false;
        }
       
        // set content
        if(this.content!=null || file!=null) {
            pageContext.clear();
          InputStream is=null;
            OutputStream os=null;
            long totalLength,contentLength;
            try {
              os=getOutputStream();
             
              if(content!=null) {
                //ReqRspUtil.setContentLength(rsp,content.length);
                contentLength=content.length;
                totalLength=content.length;
                     is=new BufferedInputStream(new ByteArrayInputStream(content))
                }
                else {
                    //ReqRspUtil.setContentLength(rsp,file.length());
                    pageContext.getConfig().getSecurityManager().checkFileLocation(file);
                    contentLength=totalLength=file.length();
                    is=IOUtil.toBufferedInputStream(file.getInputStream());
                }
             
              // write
              if(!hasRanges)
                IOUtil.copy(is,os,false,false);
              else {
                contentLength=0;
                long off,len,to;
                for(int i=0;i<ranges.length;i++) {
                  off=ranges[i].from;
                  if(ranges[i].to==-1) {
                    len=-1;
                    to=totalLength-1;
                  }
                  else {
                    to=ranges[i].to;
                    if(to>=totalLength)to=totalLength-1;
                    len=to-ranges[i].from+1;
                  }
                  rsp.addHeader("Content-Range", "bytes "+off+"-"+to+"/"+Caster.toString(totalLength));
                  rsp.setStatus(206);
                  //print.e("Content-Range: bytes "+off+"-"+to+"/"+Caster.toString(totalLength));
                  contentLength+=to-off+1L;
                        // ReqRspUtil.setContentLength(rsp,len);
                  IOUtil.copy(is, os,off,len);
                }
              }
              if(!(os instanceof GZIPOutputStream))
                ReqRspUtil.setContentLength(rsp,contentLength);
            }
            catch(IOException ioe) {}
            finally {
                IOUtil.flushEL(os);
                IOUtil.closeEL(is,os);
                if(deletefile && file!=null) ResourceUtil.removeEL(file, true);
                ((PageContextImpl)pageContext).getRootOut().setClosed(true);
            }
            throw new PostContentAbort();
        }
        // clear current content
        else if(reset)pageContext.clear();
       
        return EVAL_BODY_INCLUDE;//EVAL_PAGE;
  }

  private OutputStream getOutputStream() throws PageException, IOException {
        try {
          return pageContext.getResponseStream();
        }
        catch(IllegalStateException ise) {
            throw new TemplateException("content is already send to user, flush");
        }
    }

    @Override
  public int doEndTag()  {
    return strFile == null ? EVAL_PAGE : SKIP_PAGE;
  }

    /**
     * sets if tag has a body or not
     * @param hasBody
     */
    public void hasBody(boolean hasBody) {
    }


  private Range[] getRanges() {
    HttpServletRequest req = pageContext.getHttpServletRequest();
    Enumeration names = req.getHeaderNames();
   
    if(names==null) return null;
    String name;
    Range[] range;
    while(names.hasMoreElements()) {
      name=(String) names.nextElement();
     
      if("range".equalsIgnoreCase(name)){
        range = getRanges(name,req.getHeader(name));
        if(range!=null) return range;
      }
    }
    return null;
  }

  private Range[] getRanges(String name,String range) {
    if(StringUtil.isEmpty(range, true)) return null;
    range=StringUtil.removeWhiteSpace(range);
    if(range.indexOf("bytes=")==0) range=range.substring(6);
    String[] arr=null;
    try {
      arr = ListUtil.toStringArray(ListUtil.listToArrayRemoveEmpty(range, ','));
    } catch (PageException e) {
      failRange(name,range);
      return null;
    }
    String item;
    int index;
    long from,to;
   
    Range[] ranges=new Range[arr.length];
    for(int i=0;i<ranges.length;i++) {
      item=arr[i].trim();
      index=item.indexOf('-');
      if(index!=-1) {
        from = Caster.toLongValue(item.substring(0,index),0);
        to = Caster.toLongValue(item.substring(index+1),-1);
        if(to!=-1 && from>to){
          failRange(name,range);
          return null;
          //throw new ExpressionException("invalid range definition, from have to bigger than to ("+from+"-"+to+")");
        }
      }
      else {
        from = Caster.toLongValue(item,0);
        to=-1;
      }
      ranges[i]=new Range(from,to);
     
      if(i>0 && ranges[i-1].to>=from){
        PrintWriter err = pageContext.getConfig().getErrWriter();
        SystemOut.printDate(err,"there is a overlapping of 2 ranges ("+ranges[i-1]+","+ranges[i]+")");
        //throw new ExpressionException("there is a overlapping of 2 ranges ("+ranges[i-1]+","+ranges[i]+")");
        return null;
      }
     
    }
    return ranges;
  }

  private void failRange(String name, String range) {
    PrintWriter err = pageContext.getConfig().getErrWriter();
    SystemOut.printDate(err,"failed to parse the header field ["+name+":"+range+"]");
  }
}

class Range {

  long from;
  long to;

  public Range(long from, long len) {
    this.from = from;
    this.to = len;
  }

  public String toString() {
    return from+"-"+to;
  }
}
TOP

Related Classes of railo.runtime.tag.Range

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.