home *** CD-ROM | disk | FTP | other *** search
/ PC World 2001 April / PCWorld_2001-04_cd.bin / Software / TemaCD / smartcache / src / request.java < prev    next >
Text File  |  2001-01-26  |  35KB  |  1,125 lines

  1. /*
  2.  *  Smart Cache, http proxy cache server
  3.  *  Copyright (C) 1998, 1999, 2000 Radim Kolar 
  4.  *
  5.  *    Smart Cache is Open Source Software; you may redistribute it
  6.  *  and/or modify it under the terms of the GNU General Public
  7.  *  License as published by the Free Software Foundation; either
  8.  *  version 2, or (at your option) any later version.
  9.  *
  10.  *    This program distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13.  *  General Public License for more details.
  14.  *
  15.  *    A copy of the GNU General Public License is available as
  16.  *  /usr/doc/copyright/GPL in the Debian GNU/Linux distribution or on
  17.  *  the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
  18.  *  can also obtain it by writing to the Free Software Foundation,
  19.  *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20.  */
  21.  
  22. import java.io.*;
  23. import java.util.*;
  24. import java.util.zip.*;
  25. import java.text.*;
  26. import java.net.*;
  27.  
  28.  
  29. /* chyby : pri POST posle HTTP1.0 hlavicku HTTP0.9 klientu */
  30.  
  31. public final class request{
  32.  
  33. /* maximal time difference for checking validity of expire<now */
  34. public static final long MAXDIFFTIME=8*60*60*1000L;
  35.  
  36. public static final byte GIF[]={ 
  37. (byte)0x47,(byte)0x49,(byte)0x46,(byte)0x38,(byte)0x39,(byte)0x61,(byte)0x01,(byte)0x00,
  38. (byte)0x01,(byte)0x00,(byte)0x80,(byte)0x00,(byte)0x00,(byte)0xff,(byte)0xff,(byte)0xff,
  39. (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x21,(byte)0xf9,(byte)0x04,(byte)0x01,(byte)0x00,
  40. (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x2c,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,
  41. (byte)0x01,(byte)0x00,(byte)0x01,(byte)0x00,(byte)0x00,(byte)0x02,(byte)0x02,(byte)0x44,
  42. (byte)0x01,(byte)0x00,(byte)0x3B
  43. };
  44.  
  45. /* quick abort config */
  46. public static int qa_minlen, qa_maxlen;
  47. public static String wafer;
  48. public static float qa_percent;
  49. public static long qa_maxtime;
  50. public static boolean append_via;
  51. public static char pnocache;
  52. public static int read_timeout;
  53. public static boolean cache_protected;
  54. public static char referer_hack;
  55. public static String default_forward_for;
  56. public static boolean remove_pragma_no_cache;
  57. public static DateFormat formatter= new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
  58. public static String fake_user_agent, fake_referer;
  59.  
  60. /* agent.log */
  61. public static String alogpatterns[];
  62. public static DataOutputStream alogfilez[];
  63. public static String alogfilenames[];
  64.  
  65. /* referer.log */
  66. public static String rlogpatterns[];
  67. public static DataOutputStream rlogfilez[];
  68. public static String rlogfilenames[];
  69.  
  70. public static boolean full_referer_log;
  71.  
  72. /* object private data */
  73. private String URL;
  74. private int method;
  75. private DataInputStream in; /* klient input */
  76. private DataOutputStream out; /* klient output */
  77. private boolean http10;
  78.  
  79. private boolean cacheable;
  80.  
  81. private long ims; /* if modified since */
  82. private long exp; /* expires */
  83.  
  84. private boolean hasCookie;
  85. private int ctsize;
  86. private String ctype;
  87. private String location;
  88. private String encoding;
  89. private boolean reload;
  90. private Vector headers;
  91. private boolean host;
  92. private int httprc;
  93. private String protocol; /* ftp, gopher, ... */
  94.  
  95. private int proxyport;
  96. private InetAddress proxyhost;
  97.  
  98. request(int met,String url,Vector head,DataInputStream in,DataOutputStream out) throws MalformedURLException
  99. {
  100.  hasCookie=false;
  101.  ims=httprc=0;
  102.  ctsize=-1;
  103.  reload=false;
  104.  cacheable=true;
  105.  this.method=met;
  106.  protocol=null;
  107.  ctype=null;
  108.  
  109.  // if(met<httpreq.REQUEST_MIN || met>httpreq.REQUEST_MAX) throw new NullPointerException("Bad Method");
  110.  URL=url;
  111.  if(head==null) { http10=false; headers=new Vector();}
  112.   else
  113.    { http10=true;
  114.      headers=head;}
  115.  this.in=in;
  116.  this.out=out;
  117.  
  118.  host=false; /* host hlavicka */
  119.  /* projdeme hlavicky, zajima nas Pragma: no-cache 
  120.   a if-modified-since
  121.  */
  122.  int i,j;
  123.  String s,s1,s2,hosth;
  124.  hosth=null;
  125.  char rhcache=referer_hack;
  126.  if(rhcache==0 && rlogpatterns!=null) rhcache=99;  /* log or fake */
  127.  int cachedsize=headers.size();
  128.  for(i=0;i<cachedsize;i++)
  129.  {
  130.     s=(String)headers.elementAt(i);
  131.     j=s.indexOf(':',0);
  132.     if(j==-1) continue;
  133.     s1=s.substring(0,j).toLowerCase();
  134.     s2=s.substring(j+1).trim();
  135. //  System.out.println("Header = "+s1);
  136.     if(s1.equals("pragma"))
  137.            {
  138.        reload=true;
  139.            if (remove_pragma_no_cache) { headers.removeElementAt(i--);cachedsize--;}
  140.            continue;
  141.            }
  142.     if(s1.equals("host")) { host=true; hosth=s2;continue;}
  143.     if(s1.equals("content-length")) 
  144.              try
  145.               {
  146.                  ctsize=Integer.valueOf(s2).intValue(); 
  147.               }
  148.               catch (Exception ignore) {}
  149.               finally { continue;}    
  150.     if(s1.equals("accept-encoding"))
  151.     {
  152.      if(s2.indexOf("gzip")>-1) encoding="gzip";
  153.      continue;
  154.     }
  155.     if(s1.equals("if-modified-since")) {
  156.                                         /* cut of file size */
  157.                                         j=s2.indexOf(';',0);
  158.                                         if(j>-1) s2=s2.substring(0,j); 
  159.                     try
  160.                     {
  161.                                         ims=new Date(s2).getTime();
  162.                     }
  163.                     catch (IllegalArgumentException baddate)
  164.                      {}
  165.                                         headers.removeElementAt(i--);
  166.                                         cachedsize--;
  167.                                         continue;
  168.                                      // System.out.println("IMS="+ims);
  169.                                       }
  170.     /* kill possible Keep-Alive */                                      
  171.     if(s1.equals("connection")) {      
  172.                                         headers.removeElementAt(i--);
  173.                                         cachedsize--;
  174.                                         continue;
  175.                                       }
  176.  
  177.     if(s1.equals("proxy-connection")) {      
  178.                                         headers.removeElementAt(i--);
  179.                                         cachedsize--;
  180.                                         continue;
  181.                                       }
  182.     if(hasCookie==false && s1.equals("cookie")) 
  183.                                       {
  184.                                        hasCookie=true;
  185.                                        continue;
  186.                                       }
  187.     if(s1.equals("authorization")) {
  188.                                         cacheable=cache_protected; // was false;
  189.                                         continue;
  190.                                       }
  191.                                       
  192.                                       
  193.     if(s1.equals("user-agent")) {       
  194.                                   /* agent.log enabled? */
  195.                                   if(alogpatterns!=null)
  196.                                   {
  197.                                     int j1=alogpatterns.length;
  198.                                     for(int ii=0;ii<j1;ii++)
  199.                                     {
  200.                                        String fragment;
  201.                                        fragment=mgr.simpleWildMatch(alogpatterns[ii], url);
  202.                                        if(fragment==null) continue;
  203.                                        DataOutputStream output;
  204.                                        output=alogfilez[ii];
  205.                                        if(output==null && alogfilenames[ii]!=null)
  206.                                           /* OPEN LOGFILE */
  207.                                           if(!httpreq.open_logfile(alogfilenames,alogfilez,ii)) break;
  208.                                            else
  209.                                             output=alogfilez[ii];
  210.                                        try
  211.                                        {
  212.                                         synchronized (output)
  213.                                         {
  214.                                          output.writeBytes(s2);
  215.                                          output.writeByte('\n');
  216.                                         }
  217.                                        }
  218.                                        catch (IOException zz1)
  219.                                         {
  220.                                          alogfilenames[ii]=null;
  221.                                         }
  222.                                        break;
  223.  
  224.                                     } /* log for */
  225.                                   } /* alog enabled? */
  226.                   /* Change User-Agent: */
  227.     
  228.                                         if(fake_user_agent!=null)
  229.                       {
  230.                                             s=fake_user_agent;
  231.                                             headers.setElementAt(s,i);
  232.                       }
  233.                                          else
  234.                        if(append_via)
  235.                             {
  236.                                s+=" via "+scache.VERSION;
  237.                                                    headers.setElementAt(s,i);
  238.                         }
  239.                                         continue;
  240.                                       }
  241.     if(rhcache>0 && s1.equals("referer")) {
  242.     
  243.                                   /* referer.log enabled? */
  244.                                   if(rlogpatterns!=null)
  245.                                   {
  246.                                     int j1=rlogpatterns.length;
  247.                                     for(int ii=0;ii<j1;ii++)
  248.                                     {
  249.                                        String fragment;
  250.                                        fragment=mgr.simpleWildMatch(rlogpatterns[ii], url);
  251.                                        if(fragment==null) continue;
  252.                                        if(full_referer_log==false)
  253.                                         { /* mini referer - log */
  254.                                          int z=url.indexOf("://");
  255.                                          if(z==-1) break;
  256.                                          int z2=url.indexOf('/',z+3);
  257.                                          if(z2!=-1)
  258.                                            if (s2.regionMatches(0,url,0,z2)) break;
  259.                                              else ;
  260.                                           else
  261.                                            if(s2.regionMatches(0,url,0,url.length())) break;
  262.                                              
  263.                                         }
  264.                                        DataOutputStream output;
  265.                                        output=rlogfilez[ii];
  266.                                        if(output==null && rlogfilenames[ii]!=null)
  267.                                           /* OPEN LOGFILE */
  268.                                           if(!httpreq.open_logfile(rlogfilenames,rlogfilez,ii)) break;
  269.                                            else
  270.                                             output=rlogfilez[ii];
  271.                                        try
  272.                                        {
  273.                                         synchronized(output)
  274.                                         {
  275.                                          output.writeBytes(s2+" -> "+url);
  276.                                          output.writeByte('\n');
  277.                                         }
  278.                                        }
  279.                                        catch (IOException zz1)
  280.                                         {
  281.                                          rlogfilenames[ii]=null;
  282.                                         }
  283.                                        break;
  284.  
  285.                                     } /* log for */
  286.                                   } /* referer log enabled? */
  287.                                   if (rhcache==99)
  288.                         {
  289.                          rhcache=0;continue;
  290.                         } 
  291.                     else
  292.                   if(fake_referer!=null)
  293.                                            s=fake_referer;
  294.                             else
  295.                                   if(rhcache==1)  /* kill referer header */
  296.                                        { headers.removeElementAt(i--);cachedsize--;rhcache=0;continue;}
  297.                                     else
  298.                                        s="Referer: "+url;
  299.                                             
  300.                                    headers.setElementAt(s,i);
  301.                                    rhcache=0; /* turn it off! */
  302.                                    continue;
  303.                                       }
  304.                                                                                                                                                         
  305.  }
  306.  /* pokud jsme http 0.9 tak pridame accept hlavicku */
  307.  if(!http10) headers.addElement("Accept: */*");
  308.  /* pro jistotu */
  309.  // headers.addElement("Connection: close");
  310.  
  311. /* - F O R W A R D E R - */
  312.  
  313.  if(url.charAt(0)=='/')
  314.   {
  315.    /* fire-up forwarder! */
  316.    protocol="http";
  317.    if(hosth!=null) URL="http://"+hosth+url;
  318.            else
  319.                    URL="http://"+default_forward_for+url;
  320.    return;
  321.   }
  322.  
  323.  if(met!=httpreq.REQUEST_CONNECT)
  324.   {
  325.    /* extract protocol */
  326.    int pos=url.indexOf("://",0);
  327.    if(pos==-1) throw new MalformedURLException();
  328.    protocol=url.substring(0,pos);
  329.    
  330.    /* check for losername/password */
  331.  try
  332.  {
  333.   int zav=url.indexOf('@',pos+3);
  334.   if(zav>0)
  335.    {
  336.     /*   @   / - ok     */ 
  337.     /*   /  @ - ignore  */
  338.     /*   @    - ok      */
  339.     int slash=url.indexOf('/',pos+3);
  340.     if( slash>zav || slash==-1 )
  341.     {
  342.      String auth="Authorization: Basic "+HTUU.encode_string(url.substring(pos+3,zav));
  343.      headers.addElement(auth);
  344.      URL=protocol+"://"+url.substring(zav+1); // remove auth string from URL
  345.      // System.err.println("new URL="+URL);
  346.     }
  347.    }
  348.  }
  349.  catch (Exception e) 
  350.    { throw new MalformedURLException();} // So no need to check index out of bounds
  351.  
  352.   } else {protocol=null;cacheable=false;}
  353.  
  354. }
  355.  
  356. final public void rewriteURL(String newURL)
  357. {
  358.  this.URL=newURL;
  359.  char rhcache=referer_hack;
  360.  if(!host && rhcache<2) return; // scan and kill old host header
  361.  String s,s1;
  362.   int cachedsize=headers.size();
  363.  
  364.   for(int i=0;i<cachedsize;i++)
  365.  {  
  366.     int j;
  367.     s=(String)headers.elementAt(i);
  368.     j=s.indexOf(':',0);
  369.     if(j==-1) continue;
  370.     s1=s.substring(0,j).toLowerCase();
  371.     // s2=s.substring(j+1).trim();
  372.     if(s1.equals("host")) {      
  373.                             headers.removeElementAt(i--);
  374.                             cachedsize--;
  375.                             continue;
  376.                            }
  377.     if(rhcache>0 && s1.equals("referer")) {
  378.                                             s="Referer: "+newURL;
  379.                                             headers.setElementAt(s,i);
  380.                                           
  381.                                             rhcache=0; /* turn it off! */
  382.                                             continue;
  383.                                       }
  384.     
  385.  }
  386.  host=false;
  387. }
  388.  
  389. final public void removeCookies()
  390. {
  391.  if(hasCookie==false) return;
  392.  // scan and kill c00kie header
  393.   String s,s1;
  394.   int cachedsize=headers.size();
  395.  
  396.   for(int i=0;i<cachedsize;i++)
  397.  {  
  398.     int j;
  399.     s=(String)headers.elementAt(i);
  400.     j=s.indexOf(':',0);
  401.     if(j==-1) continue;
  402.     s1=s.substring(0,j).toLowerCase();
  403.     // s2=s.substring(j+1).trim();
  404.     if(s1.equals("cookie")) {      
  405.                             if(wafer==null)
  406.                               {
  407.                                 headers.removeElementAt(i--);
  408.                                 cachedsize--;
  409.                               }
  410.                               else
  411.                               {headers.setElementAt(wafer,i);}
  412.                             continue;
  413.                            }
  414.     
  415.  }
  416. }
  417.  
  418.  
  419. final public void addHost(String hostname,String port)
  420. {
  421.  if(host) return; /* uz ji mame - nemenime */
  422.  if(port==null)  headers.addElement("Host: "+hostname);
  423.   else
  424.    headers.addElement("Host: "+hostname+":"+port);
  425.   
  426.  host=true;
  427. }
  428.  
  429. final public int getMethod()
  430. {
  431.  return method;
  432. }
  433.  
  434. final public boolean requestReload()
  435. {
  436.  return reload;
  437. }
  438. final public String getURL()
  439. {
  440.  return URL;
  441. }
  442.  
  443. /* nastavi cil - proxy nebo cilovy server, to je jedno */
  444. final public void setTarget(InetAddress proxyhost, int proxyport)
  445. {
  446.  this.proxyhost=proxyhost;
  447.  this.proxyport=proxyport;
  448. }
  449.  
  450. final public Socket connectToHost() throws IOException
  451. {
  452.  Socket s;
  453.  if(proxyhost==null) throw new java.net.UnknownHostException();
  454.  s=new Socket(proxyhost,proxyport);
  455.  try
  456.  {
  457.    s.setSoTimeout(read_timeout);
  458.  }
  459.  catch (java.net.SocketException ignore) {}
  460.  return s;
  461. }
  462.  
  463. final public void handle_connect() throws IOException
  464. {
  465.  Socket toserver=null;
  466.  try
  467.   {
  468.    toserver=connectToHost();
  469.   }
  470.   catch (IOException e)
  471.    {
  472.     send_error(500,"Connect to server failed. CONNECT request canceled.");
  473.     return;
  474.    }
  475.   DataInputStream sin=new DataInputStream (new BufferedInputStream(toserver.getInputStream()));
  476.   DataOutputStream sou=new DataOutputStream(new BufferedOutputStream(toserver.getOutputStream()));
  477.   out.writeBytes("HTTP/1.0 200 Connection established\r\nProxy-agent: ");
  478.   out.writeBytes(scache.VERSION);
  479.   out.writeBytes("\r\n\r\n");
  480.   out.flush();
  481.  
  482.   /* buffer */
  483.   byte b[]=new byte[512];
  484.   int rb;
  485.   
  486.   Thread worker=new Thread(new InOut(sin,out,Thread.currentThread()));
  487.   worker.start();
  488.   
  489.   mainloop:while(true)
  490.   {
  491.      if(Thread.interrupted()) break;
  492.      rb=in.read(b,0,512);
  493.      if(rb==-1) break mainloop;
  494.      sou.write(b,0,rb);
  495.      sou.flush();
  496.   }
  497.   worker.interrupt();
  498.   toserver.close();
  499.   close();
  500.   return;
  501. }
  502.  
  503. final public void handle_options() throws IOException
  504. {
  505.   int maxforwards=get_and_update_maxforwards();
  506.   if(maxforwards==0)
  507.     {
  508.       /* reply to OPTIONS Message */
  509.       Vector oldheaders=(Vector)headers.clone();
  510.       make_headers(200,null,null,null,0,0,0);
  511.       headers.addElement("Allow: GET, HEAD, OPTIONS, TRACE");
  512.       send_headers();
  513.       out.close();
  514.     }
  515.     else 
  516.       direct_request(false);
  517. }
  518. final public void handle_trace() throws IOException
  519. {
  520.   int maxforwards=get_and_update_maxforwards();
  521.   if(maxforwards==0)
  522.     {
  523.       /* reply to trace Message */
  524.       Vector oldheaders=(Vector)headers.clone();
  525.       make_headers(200,"message/http",null,null,0,0,0);
  526.       send_headers();
  527.       StringBuffer sb=new StringBuffer(1024);
  528.       sb.append(httpreq.methodToString(method));
  529.       sb.append(" ");
  530.       sb.append(URL);
  531.       if(http10==true)
  532.       {
  533.       sb.append(" HTTP/1.0\r\n");
  534.       int szc=oldheaders.size();
  535.       for(int i=0;i<szc;i++)
  536.       {
  537.         sb.append((String)oldheaders.elementAt(i));
  538.     sb.append("\r\n");
  539.       }
  540.       } else sb.append("\r\n");
  541.       out.writeBytes(sb.toString());
  542.       out.close();
  543.     }
  544.     else 
  545.       direct_request(false);
  546. }
  547.  
  548. final private int get_and_update_maxforwards()
  549. {
  550.   int maxforwards=-1;
  551.  String s,s1;
  552.   int cachedsize=headers.size();
  553.   for(int i=0;i<cachedsize;i++)
  554.   {
  555.     int j;
  556.     s=(String)headers.elementAt(i);
  557.     j=s.indexOf(':',0);
  558.     if(j==-1) continue;
  559.     s1=s.substring(0,j).toLowerCase();
  560.     if(s1.equals("max-forwards")) {
  561.                             try
  562.                 {
  563.                               String s2=s.substring(j+1).trim();
  564.                               maxforwards=Integer.valueOf(s2).intValue();
  565.                 }
  566.                 catch (NumberFormatException nfe) {continue;}
  567.                             if(maxforwards>0) headers.setElementAt("Max-Forwards: "+(maxforwards-1),i);
  568.                             return maxforwards;
  569.                            }
  570.  }
  571.  return -1; // NONE
  572. }
  573.  
  574. final public void direct_request(boolean datafromclient) throws IOException
  575. {
  576.   DataInputStream sin=null;
  577.   DataOutputStream sou=null;
  578.   Socket toserver=null;
  579.  try{
  580.   toserver=connectToHost();
  581.   sin=new DataInputStream (new BufferedInputStream(toserver.getInputStream()));
  582.   sou=new DataOutputStream(new BufferedOutputStream(toserver.getOutputStream()));
  583.  send_request(sou);
  584.  }
  585.  catch (IOException e)
  586.   { send_error(500,"Connect to server failed. "+
  587.     (method==httpreq.REQUEST_POST? "POST":
  588.      (method==httpreq.REQUEST_DELETE? "DELETE": 
  589.       (method==httpreq.REQUEST_PUT? "PUT": 
  590.        (method==httpreq.REQUEST_OPTIONS? "OPTIONS": 
  591.         (method==httpreq.REQUEST_TRACE? "TRACE": "UNKNOWN")
  592.        )
  593.       )
  594.      )
  595.     )+" request canceled.");return; }
  596.  try{
  597.  
  598.  /* pokud nemame ctsize, tak cteme az na konec */
  599.  /* precteme data z klienta a odesleme je na server */
  600.  if(datafromclient) {
  601.      while(ctsize!=0)
  602.      {
  603.     //  System.out.println(ctsize);
  604.       byte b[]=new byte[512];
  605.       int rb;
  606.       if(ctsize<0) rb=in.read(b); 
  607.          else
  608.           rb=in.read(b,0,Math.min(ctsize,512)); 
  609.       if(rb==-1) break; /* konec dat! */
  610.       ctsize-=rb;
  611.       sou.write(b,0,rb);
  612.      }
  613.      sou.writeByte(0); // Zbynek Pospichal lamer detector
  614.                // Some versions of Netscape does this for fixing badly
  615.                // written CGI scripts
  616.      sou.flush();
  617.  }
  618.  /* otocime to, cteme data ze serveru a posilame je klientu */
  619.  while(true)
  620.  {
  621.   byte b[]=new byte[512];
  622.   int rb;
  623.   rb=sin.read(b); 
  624.   if(rb==-1) break; /* konec dat! */
  625.   out.write(b,0,rb);
  626.  }
  627.  
  628.  sou.close();
  629.  sin.close();
  630.  toserver.close(); 
  631.  /* zavrit klienta */
  632.  out.close();
  633.  in.close();
  634.  }
  635.  catch (IOException e1) { toserver.close();}
  636. }
  637.  
  638.         
  639. final public void send_request(DataOutputStream sout) throws IOException
  640. {
  641.  String met=null;
  642.  met=httpreq.methodToString(method);
  643.  sout.writeBytes(met+" "+URL+" HTTP/1.0\r\n");
  644.  
  645.  int hscache=headers.size();
  646.  for(int i=0;i<hscache;i++)
  647.    { sout.writeBytes((String)headers.elementAt(i));
  648.      sout.writeBytes("\r\n");
  649.    }
  650.  sout.writeBytes("\r\n");  
  651.  sout.flush();
  652. }
  653.  
  654. final public void close() throws IOException
  655. {
  656.  out.flush();
  657.  // in.close();
  658. }
  659.  
  660. /* reads headers from server and parses it */
  661. final public void read_headers(DataInputStream in) throws IOException
  662. {
  663.  headers=new Vector();
  664.  ims=exp=0; /* pro last modified */
  665.  ctsize=-1;
  666.  encoding=null;
  667.  // ctype="application/octet-stream";
  668.  ctype="text/html"; // like Mozilla does
  669.  reload=false; /* pouzivano pro pragma: no-cache */
  670.  String line;
  671.  line=in.readLine(); /* HTTP/1.0 XX OK */
  672.  if(line==null) 
  673.                   try{
  674.                         in.close();
  675.                      }
  676.                   catch(IOException e) {}
  677.                   finally
  678.                   { throw new EOFException("Cannot read http return code");}
  679.                   
  680.  /* precteme si tedy kod */
  681.  StringTokenizer st;
  682.  st=new StringTokenizer(line);
  683.  /* tady to spadne pri remote HTTP 0.9 serveru */
  684.  try
  685.  {
  686.    st.nextToken(); /* http/1.0 - nezajimave */
  687.    httprc=Integer.valueOf(st.nextToken()).intValue();
  688.  }
  689.  catch (Exception http09)
  690.  { /* 502 - Bad gateway */
  691.   send_error(502,"Remote servers talking with old HTTP 0.9 protocol are not supported.");return; 
  692.  }
  693.  
  694.  int pnci=-1;
  695.  int expi=-1;
  696.  /* cteme hlavicky */
  697.   while(true)
  698.     { 
  699.       int j;
  700.       String s1,s2;
  701.       line=in.readLine();
  702.       if(line==null) break;
  703.       if(line.length()==0) break;
  704.       headers.addElement(line);
  705.     j=line.indexOf(':',0);
  706.     if(j==-1) continue;
  707.     s1=line.substring(0,j).toLowerCase();
  708.     s2=line.substring(j+1).trim();
  709.     if(s1.equals("pragma")) { reload=true;pnci=headers.size()-1;continue;}
  710.     if(s1.equals("content-length")) 
  711.              try
  712.               {
  713.                  ctsize=Integer.valueOf(s2).intValue(); 
  714.               }
  715.               catch (Exception ignore) {}
  716.               finally { continue;}    
  717.               
  718.     if(s1.equals("content-type")) { ctype=s2; continue;}    
  719.     if(s1.equals("content-encoding")) { encoding=s2; continue;}    
  720.     if(s1.equals("location")) { location=s2; continue;}        
  721.     
  722.     if(s1.equals("last-modified")) {     
  723.                                         try{
  724.                                         ims=new Date(s2).getTime();
  725.                                         }
  726.                                         catch (IllegalArgumentException e)
  727.                                          { ims=0;}
  728.                                         continue;
  729.                                       }
  730.  
  731.     if(s1.equals("expires")) {          try{
  732.                                         exp=new Date(s2).getTime();
  733.                                         }
  734.                                         catch (IllegalArgumentException e)
  735.                                          { exp=System.currentTimeMillis()+MAXDIFFTIME;}
  736.                     finally
  737.                     {
  738.                       expi=headers.size()-1;
  739.                       continue;
  740.                     }
  741.                                       }
  742.  
  743.  } /* hlavicky */
  744.  if(method!=httpreq.REQUEST_GET) cacheable=false; /* CACHE ONLY GET REQUESTS */
  745.  if(exp!=0)
  746.      {
  747.       /* try to get now from Date: header */
  748.       long now=0;
  749.       for(int i=headers.size()-1;i>=0;i--)
  750.       {
  751.        String h,s1,s2;
  752.        int j;
  753.        h=(String)headers.elementAt(i);
  754.        j=line.indexOf(':',0);
  755.        if(j==-1) continue;
  756.        s1=line.substring(0,j).toLowerCase();
  757.        if(s1.equals("date")) {     
  758.                                         try{
  759.                                         s2=line.substring(j+1).trim();
  760.                                         now=new Date(s2).getTime();
  761.                                         }
  762.                                         catch (IllegalArgumentException e)
  763.                                          { now=0;}
  764.                                         break;
  765.                                       }
  766.       } /* Date: header hunting */
  767.       if(now==0) now=System.currentTimeMillis();
  768.       if(exp<=now) 
  769.       {  /* Expire date NOW or in the past is the same as Pragma: no-cache */
  770.  
  771.           exp=0; // kill expire info
  772.       reload=true;
  773.           headers.removeElementAt(expi); /* KILL EXPIRE!!!*/
  774.           if(pnci>expi) pnci--;
  775.        else
  776.              if(pnci==-1) /* if removed Expire, generate PNC */
  777.                       { headers.addElement("Pragma: no-cache");
  778.                         pnci=headers.size()-1;
  779.               }
  780.       } /* exp<=now, killed */
  781.       else
  782.        { /* fix expire to out local time */
  783.          exp=System.currentTimeMillis()+(exp-now);
  784.        }
  785.      }  
  786.  if(cacheable==false) { return;}
  787.  /* set Expire to MaxAge, prevents from early redirect refresh */
  788.  if( (httprc==301 || httprc==300) && exp==0) exp=System.currentTimeMillis()+mgr.max_age;
  789.  if(!reload) {
  790.                if(cacheobject.generate_lastmod==2 && ims==0 )
  791.                 {
  792.                  headers.addElement("Last-Modified: "+printDate(new Date()));
  793.                 }
  794.                return;
  795.              }
  796.  
  797.  /* CHECK:
  798.       if we are removing PNC, could we also kill GOOD expire header in future?
  799.  
  800.       NO: PNC and Good Expire should never happen
  801.       */
  802.  switch(pnocache)
  803.  {
  804.   case 1: 
  805.         reload=false;
  806.         if(pnci>=0) headers.removeElementAt(pnci);
  807.     break;
  808.   case 2: 
  809.         if(ctype.startsWith("image")) 
  810.     {
  811.         reload=false;
  812.                 if(pnci>=0) headers.removeElementAt(pnci);
  813.     }
  814.     break;
  815.   case 3:
  816.          if(!ctype.startsWith("text")) 
  817.      {
  818.          reload=false;
  819.                  if(pnci>=0) headers.removeElementAt(pnci);
  820.      }
  821.      break;
  822.   case 4:
  823.        if(location==null)
  824.          {
  825.          reload=false;
  826.                  if(pnci>=0) headers.removeElementAt(pnci);
  827.          }
  828.          break;
  829.  }
  830.  /* set uncacheable */
  831.  if(reload==true) cacheable=false;
  832. }
  833.  
  834. final public void transfer_object(InputStream sin, OutputStream file,cachedir dir) throws IOException
  835. {
  836.  int read=0;
  837.  int i;
  838.  long whenaborted;
  839.  boolean clientdead;
  840.  boolean filedead;
  841.  byte b[]=new byte[4096];
  842.  clientdead=filedead=false;
  843.  whenaborted=0;
  844.  while(true)
  845.  {
  846.   /* precist */
  847.   try{
  848.   i=sin.read(b);
  849.   }
  850.   catch (IOException e) { if(file!=null) file.close(); 
  851.                           ctsize=read;
  852.                           throw e;
  853.                         }
  854.   if(i==-1) break; /* end of Data stream? */
  855.   
  856.   read+=i;
  857.   if(file!=null) { dir.dirty=true;
  858.                    try {
  859.                         file.write(b,0,i);
  860.                }
  861.            catch(IOException writerr)
  862.            {
  863.             try
  864.              {
  865.               file.close();
  866.              }
  867.             catch (IOException ignore) {}
  868.             finally { file=null;filedead=true;}
  869.            }
  870.            }
  871.   
  872.   /* zapisovat do klienta opatrne! */
  873.   try{
  874.   if(!clientdead) out.write(b,0,i);
  875.    else
  876.     if(qa_minlen<0 && System.currentTimeMillis()-whenaborted>qa_maxtime) 
  877.                             { ctsize=read;
  878.                   sin.close();
  879.                             // System.out.println("Transfer of "+this.URL+" terminated. (aborted transfer time exceed)");
  880.                             throw new IOException("Aborted transfer time timed out");}
  881.   }
  882.   catch(IOException e) { if(file==null) /* not caching */
  883.                                 { ctsize=read;sin.close();throw e; }
  884.                          if(CheckQuickAbort(read)) { ctsize=read;file.close();sin.close();throw e;}
  885.                          // System.out.println("client-close!");
  886.                          clientdead=true;
  887.                          whenaborted=System.currentTimeMillis();
  888.                          }
  889.  } 
  890.  sin.close();
  891.  
  892.  try{
  893.  out.flush();
  894.  } catch (IOException ignore) {}
  895.  
  896.  if(file!=null) { file.flush();file.close();}
  897.  
  898.  if(ctsize>0 && read<ctsize) {
  899.                                 ctsize=read;
  900.                                 throw new IOException("Object too small");
  901.                  }
  902.  if(filedead) throw new IOException("Write error (disk full?)");                 
  903.  // waz !=ctsize
  904.  ctsize=read;
  905. }
  906.  
  907. /* add if-mod-since header */
  908. final public void add_ims()
  909. {
  910.  add_ims(ims);
  911. }
  912.  
  913. final public void add_ims(long when)
  914. {
  915.  if(when==0) return;
  916.  headers.insertElementAt("If-Modified-Since: "+printDate(new Date(when)),0); 
  917. }
  918.  
  919. final public void add_header(String header)
  920. {
  921.  if(header==null) return;
  922.  headers.addElement(header); 
  923. }
  924.  
  925. final public int getRc()
  926. {
  927.  return httprc;
  928. }
  929.  
  930. /* odesle obdrzene hlavicky od serveru klientu */
  931. /* nereportuje io chyby */
  932. final public void send_headers()
  933. {
  934.  if(!http10) return;
  935.  try{
  936.  out.writeBytes("HTTP/1.0 "+httprc+" OK\r\n");
  937.  int headerscache=headers.size();
  938.  for(int i=0;i<headerscache;i++)
  939.    { out.writeBytes((String)headers.elementAt(i));
  940.      out.writeBytes("\r\n");
  941.    }
  942.  out.writeBytes("\r\n");  
  943.  out.flush();
  944.  }
  945.  catch (IOException e)
  946.    {
  947.     try{
  948.     out.close();
  949.     }
  950.     catch (IOException e1) {}
  951.    }
  952. }
  953.  
  954. final public void make_headers(int rc,String ctype,String enc,String loc,int sz,long lm,long exp)
  955. {
  956.  httprc=rc;
  957.  headers=new Vector();
  958.  Date d=new Date();
  959.  if(ctype!=null) headers.addElement("Content-Type: "+ctype);
  960.  if(enc!=null) headers.addElement("Content-Encoding: "+enc);
  961.  if(sz>0) headers.addElement("Content-Length: "+sz);
  962.  if(lm!=0) headers.addElement("Last-Modified: "+printDate(new Date(lm)));
  963.  
  964.  /* 
  965.    SETUP: Uncoment following line if you want to SC don't send Expired: 
  966.      headers from the past.
  967.    if(exp>d.getTime()) headers.addElement("Expires: "+printDate(new Date(exp)));
  968.  */
  969.  if(exp>0) headers.addElement("Expires: "+printDate(new Date(exp)));
  970.  if(loc!=null) headers.addElement("Location: "+loc);
  971.  /* prevent TCP/IP FIN2 state !! */
  972.  headers.addElement("Connection: close");
  973.  headers.addElement("Proxy-Connection: close");
  974.  /* less important , may be ignored in old Netscapes */
  975.  headers.addElement("Date: "+printDate(d));
  976.  headers.addElement("Server: "+scache.VERSION);
  977. }
  978.  
  979. final public void setRequestTo(String URL)
  980. {
  981.  this.URL=URL;
  982. }
  983.  
  984. final public long getIms()
  985. {
  986.  return ims;
  987. }
  988.  
  989. final public long getExpires()
  990. {
  991.  return exp;
  992. }
  993.  
  994.  
  995. final public void clearIms()
  996. {
  997.  ims=0;
  998. }
  999.  
  1000. final public String getCtype()
  1001. {
  1002.  return ctype;
  1003. }
  1004.  
  1005. final public int getCsize()
  1006. {
  1007.  return ctsize;
  1008. }
  1009.  
  1010. final public String getLocation()
  1011. {
  1012.  return location;
  1013. }
  1014.  
  1015. final public String getEncoding()
  1016. {
  1017.  return encoding;
  1018. }
  1019.  
  1020. final public void send_error(int errorrc,String msg) throws IOException
  1021. {
  1022.  httpreq.server_error(http10,errorrc,msg, out);
  1023. }
  1024.  
  1025.  
  1026. /* return true if the request should be aborted */
  1027.  
  1028. final private boolean
  1029. CheckQuickAbort(int readb)
  1030. {
  1031.     if (!cacheable) 
  1032.           {
  1033.         // System.out.println("Non cacheable data?? - Abort");
  1034.             return true;
  1035.       }
  1036.  
  1037.     if (qa_minlen < 0)
  1038.     {
  1039.      /* disabled */
  1040.      // System.out.println("QUICK_ABORT DISABLED - continuing");
  1041.      return false;
  1042.     }
  1043.  
  1044.     if(ctsize!=-1) /* Content-size is known */
  1045.     {        
  1046.     if (readb > ctsize)
  1047.         {
  1048.     /* bad content length */
  1049.     // System.out.println("ABORTING more data than expected in header");
  1050.     return true;
  1051.     }
  1052.     
  1053.     if ((ctsize - readb) < qa_minlen)
  1054.         {
  1055.     /* only little more left */
  1056.     // System.out.println("Continue, more than minlen bytes left.");
  1057.     return false;
  1058.     }
  1059.     if ((ctsize - readb) > qa_maxlen)
  1060.        {
  1061.     /* too much left to go */
  1062.     // System.out.println("Abort, more than maxlen bytes left.");
  1063.     return true;
  1064.        }
  1065.     if ( (float)readb / ctsize > qa_percent)
  1066.         {
  1067.     /* past point of no return */
  1068.     // System.out.println("Percentage ratio is good, continue");
  1069.     return false;
  1070.     } else
  1071.     {
  1072.         // System.out.println("Percentage ratio is bad, abort");
  1073.     return true;
  1074.     }
  1075.     }
  1076.     // System.out.println("Unknown data size?, abort");
  1077.     return true; /* STOP DOWNLOADING! */
  1078.     
  1079.     /*
  1080.     return false;  no dobra povolime to, ale vypadne to na timeout nebo
  1081.     na pocet dat */
  1082. }
  1083.  
  1084. final public static String printDate(Date d)
  1085. {
  1086.  
  1087.  try
  1088.   {
  1089.    return formatter.format(d);
  1090.   }
  1091.  catch (Exception z)
  1092.   {
  1093.    z.printStackTrace();
  1094.    System.out.println("[JAVA_INTERNAL_ERROR] SimpleDateFormat.format(d) failed\nDate format=EEE, dd MMM yyyy HH:mm:ss 'GMT' BadDate(formated)="+d+" time(long)="+d.getTime()+" THIS IS A JAVA BUG.\nREPORT TO      http://java.sun.com/cgi-bin/bugreport.cgi");
  1095.   }
  1096.   return null;
  1097. }
  1098.  
  1099. final public boolean canCache()
  1100. {
  1101.  return cacheable;
  1102. }
  1103.  
  1104. final public void nocache()
  1105. {
  1106.  cacheable=false;
  1107. }
  1108.  
  1109. final public void sendString(String arg) throws IOException
  1110. {
  1111.  out.writeBytes(arg);
  1112. }
  1113.  
  1114. final public void sendBytes(byte b[]) throws IOException
  1115. {
  1116.  out.write(b);
  1117. }
  1118.  
  1119. public final String getProtocol()
  1120. {
  1121.  return protocol;
  1122. }
  1123.  
  1124. }
  1125.