KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > server > hmux > HmuxRequest


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.server.hmux;
31
32 import com.caucho.log.Log;
33 import com.caucho.server.cluster.BackingManager;
34 import com.caucho.server.cluster.Cluster;
35 import com.caucho.server.connection.AbstractHttpRequest;
36 import com.caucho.server.connection.Connection;
37 import com.caucho.server.dispatch.DispatchServer;
38 import com.caucho.server.dispatch.Invocation;
39 import com.caucho.server.dispatch.InvocationDecoder;
40 import com.caucho.server.http.InvocationKey;
41 import com.caucho.server.port.ServerRequest;
42 import com.caucho.server.webapp.ErrorPageManager;
43 import com.caucho.util.ByteBuffer;
44 import com.caucho.util.CharBuffer;
45 import com.caucho.util.CharSegment;
46 import com.caucho.vfs.ClientDisconnectException;
47 import com.caucho.vfs.ReadStream;
48 import com.caucho.vfs.StreamImpl;
49 import com.caucho.vfs.WriteStream;
50
51 import java.io.IOException JavaDoc;
52 import java.io.InputStream JavaDoc;
53 import java.io.InterruptedIOException JavaDoc;
54 import java.net.InetAddress JavaDoc;
55 import java.security.cert.CertificateFactory JavaDoc;
56 import java.security.cert.X509Certificate JavaDoc;
57 import java.util.ArrayList JavaDoc;
58 import java.util.Collections JavaDoc;
59 import java.util.Enumeration JavaDoc;
60 import java.util.HashSet JavaDoc;
61 import java.util.logging.Level JavaDoc;
62 import java.util.logging.Logger JavaDoc;
63
64 /**
65  * Handles requests from a remote dispatcher. For example, mod_caucho
66  * and the IIS plugin use this protocol to talk to the backend.
67  *
68  * <p>Packets are straightforward:
69  * <pre>code l2 l1 l0 contents</pre>
70  * Where code is the code of the packet and l2-0 give 12 bits of length.
71  *
72  * <p>The protocol is designed to allow pipelining and buffering whenever
73  * possible. So most commands are not acked. Data from the frontend (POST)
74  * does need acks to prevent deadlocks at either end while waiting
75  * for new data.
76  *
77  * <p>The overriding protocol is controlled by requests from the
78  * frontend server.
79  *
80  * <p>A ping request:
81  * <pre>
82  * Frontend Backend
83  * CSE_PING
84  * CSE_END
85  * CSE_END
86  * <pre>
87  *
88  * <p>A GET request:
89  * <pre>
90  * Frontend Backend
91  * CSE_METHOD
92  * ...
93  * CSE_HEADER/CSE_VALUE
94  * CSE_END
95  * CSE_DATA
96  * CSE_DATA
97  * CSE_END
98  * <pre>
99  *
100  * <p>Short POST:
101  * <pre>
102  * Frontend Backend
103  * CSE_METHOD
104  * ...
105  * CSE_HEADER/CSE_VALUE
106  * CSE_DATA
107  * CSE_END
108  * CSE_DATA
109  * CSE_DATA
110  * CSE_END
111  * <pre>
112  *
113  * <p>Long POST:
114  * <pre>
115  * Frontend Backend
116  * CSE_METHOD
117  * ...
118  * CSE_HEADER/CSE_VALUE
119  * CSE_DATA
120  * CSE_DATA (optional)
121  * CSE_DATA
122  * CSE_ACK
123  * CSE_DATA (optional)
124  * CSE_DATA
125  * CSE_ACK
126  * CSE_END
127  * CSE_DATA
128  * CSE_END
129  * <pre>
130  *
131  */

132 public class HmuxRequest extends AbstractHttpRequest
133   implements ServerRequest {
134   private static final Logger JavaDoc log = Log.open(HmuxRequest.class);
135
136   // HMUX channel control codes
137
public static final int HMUX_CHANNEL = 'C';
138   public static final int HMUX_ACK = 'A';
139   public static final int HMUX_ERROR = 'E';
140   public static final int HMUX_YIELD = 'Y';
141   public static final int HMUX_QUIT = 'Q';
142   public static final int HMUX_EXIT = 'X';
143
144   public static final int HMUX_DATA = 'D';
145   public static final int HMUX_URI = 'U';
146   public static final int HMUX_STRING = 'S';
147   public static final int HMUX_HEADER = 'H';
148   public static final int HMUX_PROTOCOL = 'P';
149   public static final int HMUX_META_HEADER = 'M';
150   
151   // The following are HTTP codes
152
public static final int CSE_NULL = '?';
153   public static final int CSE_PATH_INFO = 'b';
154   public static final int CSE_PROTOCOL = 'c';
155   public static final int CSE_REMOTE_USER = 'd';
156   public static final int CSE_QUERY_STRING = 'e';
157   public static final int HMUX_FLUSH = 'f';
158   public static final int CSE_SERVER_PORT = 'g';
159   public static final int CSE_REMOTE_HOST = 'h';
160   public static final int CSE_REMOTE_ADDR = 'i';
161   public static final int CSE_REMOTE_PORT = 'j';
162   public static final int CSE_REAL_PATH = 'k';
163   public static final int CSE_SCRIPT_FILENAME = 'l';
164   public static final int HMUX_METHOD = 'm';
165   public static final int CSE_AUTH_TYPE = 'n';
166   public static final int CSE_URI = 'o';
167   public static final int CSE_CONTENT_LENGTH = 'p';
168   public static final int CSE_CONTENT_TYPE = 'q';
169   public static final int CSE_IS_SECURE = 'r';
170   public static final int HMUX_STATUS = 's';
171   public static final int CSE_CLIENT_CERT = 't';
172   public static final int CSE_SERVER_TYPE = 'u';
173   public static final int HMUX_SERVER_NAME = 'v';
174   
175   public static final int CSE_SEND_HEADER = 'G';
176
177   public static final int CSE_DATA = 'D';
178   public static final int CSE_FLUSH = 'F';
179   public static final int CSE_KEEPALIVE = 'K';
180   public static final int CSE_ACK = 'A';
181   public static final int CSE_END = 'Z';
182   public static final int CSE_CLOSE = 'X';
183
184   // other, specialized protocols
185
public static final int CSE_QUERY = 'Q';
186   public static final int CSE_PING = 'P';
187
188   public static final int HMUX_CLUSTER_PROTOCOL = 0x101;
189   public static final int HMUX_DISPATCH_PROTOCOL = 0x102;
190
191   public enum ProtocolResult {
192     QUIT,
193     EXIT,
194     YIELD
195   };
196   
197   static final int HTTP_0_9 = 0x0009;
198   static final int HTTP_1_0 = 0x0100;
199   static final int HTTP_1_1 = 0x0101;
200
201   private static final int HEADER_CAPACITY = 256;
202   
203   static final CharBuffer _getCb = new CharBuffer("GET");
204   static final CharBuffer _headCb = new CharBuffer("HEAD");
205   static final CharBuffer _postCb = new CharBuffer("POST");
206
207   private final CharBuffer _method; // "GET"
208
private String JavaDoc _methodString; // "GET"
209
// private CharBuffer scheme; // "http:"
210
private CharBuffer _host; // www.caucho.com
211
private int _port; // :80
212

213   private ByteBuffer _uri; // "/path/test.jsp/Junk"
214
private CharBuffer _protocol; // "HTTP/1.0"
215
private int _version;
216
217   private CharBuffer _remoteAddr;
218   private CharBuffer _remoteHost;
219   private CharBuffer _serverName;
220   private CharBuffer _serverPort;
221   private CharBuffer _remotePort;
222   
223   private boolean _isSecure;
224   private ByteBuffer _clientCert;
225
226   private CharBuffer []_headerKeys;
227   private CharBuffer []_headerValues;
228   private int _headerSize;
229
230   private byte []_lengthBuf;
231
232   private int _serverType;
233
234   // write stream from the connection
235
private WriteStream _rawWrite;
236   // servlet write stream
237
private WriteStream _writeStream;
238   
239   // StreamImpl to break reads and writes to the underlying protocol
240
private ServletFilter _filter;
241   private int _pendingData;
242
243   private InvocationKey _invocationKey = new InvocationKey();
244
245   private CharBuffer _cb1;
246   private CharBuffer _cb2;
247   private boolean _hasRequest;
248
249   private AbstractClusterRequest _clusterRequest;
250   private HmuxDispatchRequest _dispatchRequest;
251   private BackingManager _backingManager;
252   private Cluster _cluster;
253   
254   private ErrorPageManager _errorManager = new ErrorPageManager();
255
256   private int _srunIndex;
257
258   public HmuxRequest(DispatchServer server, Connection conn)
259   {
260     super(server, conn);
261
262     _response = new HmuxResponse(this);
263
264     _rawWrite = conn.getWriteStream();
265     _writeStream = new WriteStream();
266     _writeStream.setReuseBuffer(true);
267
268     // XXX: response.setIgnoreClientDisconnect(server.getIgnoreClientDisconnect());
269

270     _cluster = Cluster.getLocal();
271     if (_cluster != null) {
272       try {
273     Class JavaDoc cl = Class.forName("com.caucho.server.hmux.HmuxClusterRequest");
274
275     _clusterRequest = (AbstractClusterRequest) cl.newInstance();
276     _clusterRequest.setRequest(this);
277     _clusterRequest.setCluster(_cluster);
278       } catch (ClassNotFoundException JavaDoc e) {
279     log.finer(e.toString());
280       } catch (Throwable JavaDoc e) {
281     log.log(Level.FINER, e.toString(), e);
282       }
283     }
284     
285     _dispatchRequest = new HmuxDispatchRequest(this);
286     
287     _uri = new ByteBuffer();
288
289     _method = new CharBuffer();
290     _host = new CharBuffer();
291     _protocol = new CharBuffer();
292
293     _headerKeys = new CharBuffer[HEADER_CAPACITY];
294     _headerValues = new CharBuffer[_headerKeys.length];
295     for (int i = 0; i < _headerKeys.length; i++) {
296       _headerKeys[i] = new CharBuffer();
297       _headerValues[i] = new CharBuffer();
298     }
299
300     _remoteHost = new CharBuffer();
301     _remoteAddr = new CharBuffer();
302     _serverName = new CharBuffer();
303     _serverPort = new CharBuffer();
304     _remotePort = new CharBuffer();
305
306     _clientCert = new ByteBuffer();
307
308     _cb1 = new CharBuffer();
309     _cb2 = new CharBuffer();
310
311     _lengthBuf = new byte[16];
312
313     _filter = new ServletFilter();
314   }
315
316   public boolean isWaitForRead()
317   {
318     return true;
319   }
320
321   /**
322    * Handles a new request. Initializes the protocol handler and
323    * the request streams.
324    *
325    * <p>Note: ClientDisconnectException must be rethrown to
326    * the caller.
327    */

328   public boolean handleRequest()
329     throws IOException JavaDoc
330   {
331     // XXX: should be moved to TcpConnection
332
Thread JavaDoc thread = Thread.currentThread();
333     thread.setContextClassLoader(_server.getClassLoader());
334     
335     if (log.isLoggable(Level.FINE))
336       log.fine(dbgId() + "start request");
337
338     _filter.init(this, _rawRead, _rawWrite);
339     _writeStream.init(_filter);
340     // _writeStream.setWritePrefix(3);
341

342     _response.init(_writeStream);
343
344     _serverType = 0;
345     _uri.setLength(0);
346     
347     boolean hasRequest = false;
348     
349     try {
350       start();
351       _response.start();
352
353       try {
354         if (! scanHeaders()) {
355           killKeepalive();
356           return false;
357         }
358         else if (_uri.size() == 0) {
359           return true;
360     }
361       } catch (InterruptedIOException JavaDoc e) {
362         killKeepalive();
363         log.fine(dbgId() + "interrupted keepalive");
364         return false;
365       }
366
367       if (_isSecure)
368         getClientCertificate();
369
370       hasRequest = true;
371       // setStartDate();
372

373       if (_server == null || _server.isDestroyed()) {
374     log.fine(dbgId() + "server is closed");
375     
376     try {
377       _writeStream.setDisableClose(false);
378       _writeStream.close();
379     } catch (Throwable JavaDoc e) {
380     }
381     
382     try {
383       _readStream.setDisableClose(false);
384       _readStream.close();
385     } catch (Throwable JavaDoc e) {
386     }
387
388     return false;
389       }
390
391       _filter.setPending(_pendingData);
392
393       try {
394         if (_method.getLength() == 0)
395           throw new RuntimeException JavaDoc("HTTP protocol exception");
396           
397         _invocationKey.init(_isSecure,
398                 getHost(), getServerPort(),
399                             _uri.getBuffer(), _uri.getLength());
400
401         Invocation invocation;
402
403         invocation = _server.getInvocation(_invocationKey);
404
405         if (invocation == null) {
406           invocation = _server.createInvocation();
407
408           if (_host != null)
409             invocation.setHost(_host.toString());
410           
411           invocation.setPort(getServerPort());
412
413           InvocationDecoder decoder = _server.getInvocationDecoder();
414
415           decoder.splitQueryAndUnescape(invocation,
416                                         _uri.getBuffer(),
417                                         _uri.getLength());
418
419           _server.buildInvocation(_invocationKey.clone(), invocation);
420         }
421
422     setInvocation(invocation);
423       
424         invocation.service(this, _response);
425       } catch (ClientDisconnectException e) {
426         throw e;
427       } catch (Throwable JavaDoc e) {
428         try {
429           _errorManager.sendServletError(e, this, _response);
430         } catch (ClientDisconnectException e1) {
431           throw e1;
432         } catch (Exception JavaDoc e1) {
433           log.log(Level.FINE, e1.toString(), e1);
434         }
435
436     return false;
437       }
438     } finally {
439       if (! hasRequest)
440     _response.setHeaderWritten(true);
441       
442       try {
443     finish();
444     _response.finish();
445       } catch (ClientDisconnectException e) {
446         throw e;
447       } catch (Exception JavaDoc e) {
448     killKeepalive();
449         log.log(Level.FINE, dbgId() + e, e);
450       }
451
452       try {
453     _writeStream.setDisableClose(false);
454     _writeStream.close();
455       } catch (ClientDisconnectException e) {
456     killKeepalive();
457         log.log(Level.FINE, dbgId() + e, e);
458
459         throw e;
460       } catch (Exception JavaDoc e) {
461     killKeepalive();
462         log.log(Level.FINE, dbgId() + e, e);
463       }
464
465       try {
466     _readStream.setDisableClose(false);
467     _readStream.close();
468       } catch (Exception JavaDoc e) {
469     killKeepalive();
470         log.log(Level.FINE, dbgId() + e, e);
471       }
472     }
473
474     boolean allowKeepalive = isKeepalive();
475     
476     if (log.isLoggable(Level.FINE)) {
477       if (allowKeepalive)
478     log.fine(dbgId() + "complete request - keepalive");
479       else
480     log.fine(dbgId() + "complete request");
481     }
482
483     return allowKeepalive;
484   }
485
486   /**
487    * Initialize the read stream from the raw stream.
488    */

489   protected boolean initStream(ReadStream readStream,
490                                ReadStream rawStream)
491     throws IOException JavaDoc
492   {
493     readStream.init(_filter, null);
494
495     return true;
496   }
497
498   private void getClientCertificate()
499   {
500     String JavaDoc cipher = getHeader("SSL_CIPHER");
501     if (cipher == null)
502       cipher = getHeader("HTTPS_CIPHER");
503     if (cipher != null)
504       setAttribute("javax.servlet.request.cipher_suite", cipher);
505     
506     String JavaDoc keySize = getHeader("SSL_CIPHER_USEKEYSIZE");
507     if (keySize == null)
508       keySize = getHeader("SSL_SECRETKEYSIZE");
509     if (keySize != null)
510       setAttribute("javax.servlet.request.key_size", keySize);
511     
512     if (_clientCert.size() == 0)
513       return;
514     
515     try {
516       CertificateFactory JavaDoc cf = CertificateFactory.getInstance("X.509");
517       InputStream JavaDoc is = _clientCert.createInputStream();
518       Object JavaDoc cert = cf.generateCertificate(is);
519       is.close();
520       setAttribute("javax.servlet.request.X509Certificate", cert);
521       setAttribute(com.caucho.server.security.AbstractAuthenticator.LOGIN_NAME,
522                    ((X509Certificate JavaDoc) cert).getSubjectDN());
523     } catch (Throwable JavaDoc e) {
524       log.log(Level.FINE, e.toString(), e);
525     }
526   }
527
528   /**
529    * Returns true for the top-level request, but false for any include()
530    * or forward()
531    */

532   public boolean isTop()
533   {
534     return true;
535   }
536   
537   protected boolean checkLogin()
538   {
539     return true;
540   }
541
542   /**
543    * Clears variables at the start of a new request.
544    */

545   protected void start()
546     throws IOException JavaDoc
547   {
548     super.start();
549
550     _method.clear();
551     _methodString = null;
552     _protocol.clear();
553     _version = 0;
554     _uri.clear();
555     _host.clear();
556     _port = 0;
557
558     _headerSize = 0;
559
560     _remoteHost.clear();
561     _remoteAddr.clear();
562     _serverName.clear();
563     _serverPort.clear();
564     _remotePort.clear();
565
566     _clientCert.clear();
567
568     _pendingData = 0;
569
570     _isSecure = false;
571   }
572   
573   /**
574    * Fills request parameters from the stream.
575    */

576   private boolean scanHeaders()
577     throws IOException JavaDoc
578   {
579     boolean hasURI = false;
580     CharBuffer cb = _cb;
581     boolean isLoggable = log.isLoggable(Level.FINE);
582     ReadStream is = _rawRead;
583     int code;
584     int len;
585
586     while (true) {
587       code = is.read();
588
589       switch (code) {
590       case -1:
591         if (isLoggable)
592           log.fine(dbgId() + "end of file");
593         return false;
594
595       case HMUX_CHANNEL:
596     int channel = (is.read() << 8) + is.read();
597     
598         if (isLoggable)
599           log.fine(dbgId() + "channel " + channel);
600     break;
601
602       case HMUX_QUIT:
603         if (isLoggable)
604           log.fine(dbgId() + (char) code + ": end of request");
605         
606         return hasURI;
607
608       case HMUX_EXIT:
609         if (isLoggable)
610           log.fine(dbgId() + (char) code + ": end of socket");
611
612         killKeepalive();
613         
614         return hasURI;
615
616       case HMUX_PROTOCOL:
617         len = (is.read() << 8) + is.read();
618
619         if (len != 4) {
620           log.fine(dbgId() + (char) code + ": protocol length (" + len + ") must be 4.");
621           killKeepalive();
622           return false;
623         }
624         
625         int value = ((is.read() << 24) +
626                      (is.read() << 16) +
627                      (is.read() << 8) +
628                      (is.read()));
629
630     int result = HMUX_EXIT;
631     boolean isKeepalive = false;
632         if (value == HMUX_CLUSTER_PROTOCOL) {
633           if (isLoggable)
634             log.fine(dbgId() + (char) code + ": cluster protocol");
635           _filter.setClientClosed(true);
636       
637       if (_server == null || _server.isDestroyed()) {
638         return false;
639       }
640       
641           result = _clusterRequest.handleRequest(is, _rawWrite);
642         }
643         else if (value == HMUX_DISPATCH_PROTOCOL) {
644           if (isLoggable)
645             log.fine(dbgId() + (char) code + ": dispatch protocol");
646           _filter.setClientClosed(true);
647       
648       if (_server == null || _server.isDestroyed()) {
649         return false;
650       }
651       
652           isKeepalive = _dispatchRequest.handleRequest(is, _rawWrite);
653
654       if (isKeepalive)
655         result = HMUX_QUIT;
656       else
657         result = HMUX_EXIT;
658         }
659     else {
660       log.fine(dbgId() + (char) code + ": unknown protocol (" + value + ")");
661       result = HMUX_EXIT;
662     }
663
664     if (result == HMUX_YIELD)
665       break;
666     else {
667       if (result == HMUX_QUIT && ! allowKeepalive())
668         result = HMUX_EXIT;
669     
670       if (result == HMUX_QUIT) {
671         _rawWrite.write(HMUX_QUIT);
672         _rawWrite.flush();
673       }
674       else {
675         _rawWrite.write(HMUX_EXIT);
676         _rawWrite.close();
677       }
678
679       return result == HMUX_QUIT;
680     }
681
682       case HMUX_URI:
683         hasURI = true;
684         len = (is.read() << 8) + is.read();
685         _uri.setLength(len);
686     _rawRead.readAll(_uri.getBuffer(), 0, len);
687     if (isLoggable)
688       log.fine(dbgId() + (char) code + ":uri " + _uri);
689     break;
690     
691       case HMUX_METHOD:
692         len = (is.read() << 8) + is.read();
693     is.readAll(_method, len);
694     if (isLoggable)
695       log.fine(dbgId() +
696                    (char) code + ":method " + _method);
697     break;
698
699       case CSE_REAL_PATH:
700         len = (is.read() << 8) + is.read();
701     _cb1.clear();
702     _rawRead.readAll(_cb1, len);
703     code = _rawRead.read();
704     if (code != HMUX_STRING)
705       throw new IOException JavaDoc("protocol expected HMUX_STRING");
706     _cb2.clear();
707     _rawRead.readAll(_cb2, readLength());
708
709     //http.setRealPath(cb1.toString(), cb2.toString());
710
if (isLoggable)
711       log.fine(dbgId() + (char) code + " " +
712                    _cb1.toString() + "->" + _cb2.toString());
713     //throw new RuntimeException();
714
break;
715
716       case CSE_REMOTE_HOST:
717         len = (is.read() << 8) + is.read();
718     _rawRead.readAll(_remoteHost, len);
719     if (isLoggable)
720       log.fine(dbgId() + (char) code + " " + _remoteHost);
721     break;
722
723       case CSE_REMOTE_ADDR:
724         len = (is.read() << 8) + is.read();
725     _rawRead.readAll(_remoteAddr, len);
726     if (isLoggable)
727       log.fine(dbgId() + (char) code + " " + _remoteAddr);
728     break;
729
730       case HMUX_SERVER_NAME:
731         len = (is.read() << 8) + is.read();
732     _rawRead.readAll(_serverName, len);
733     if (isLoggable)
734       log.fine(dbgId() + (char) code + " server-host: " + _serverName);
735     break;
736
737       case CSE_REMOTE_PORT:
738         len = (is.read() << 8) + is.read();
739     _rawRead.readAll(_remotePort, len);
740     if (isLoggable)
741       log.fine(dbgId() + (char) code +
742                   " remote-port: " + _remotePort);
743     break;
744
745       case CSE_SERVER_PORT:
746         len = (is.read() << 8) + is.read();
747     _rawRead.readAll(_serverPort, len);
748     if (isLoggable)
749       log.fine(dbgId() + (char) code +
750                   " server-port: " + _serverPort);
751     break;
752     
753       case CSE_QUERY_STRING:
754         len = (is.read() << 8) + is.read();
755         if (len > 0) {
756           _uri.add('?');
757           _uri.ensureCapacity(_uri.getLength() + len);
758           _rawRead.readAll(_uri.getBuffer(), _uri.getLength(), len);
759           _uri.setLength(_uri.getLength() + len);
760         }
761     break;
762
763       case CSE_PROTOCOL:
764         len = (is.read() << 8) + is.read();
765     _rawRead.readAll(_protocol, len);
766     if (isLoggable)
767       log.fine(dbgId() + (char) code + " protocol: " + _protocol);
768     for (int i = 0; i < len; i++) {
769       char ch = _protocol.charAt(i);
770       if (ch >= '0' && ch <= '9')
771         _version = 16 * _version + ch - '0';
772       else if (ch == '.')
773         _version = 16 * _version;
774     }
775     break;
776
777       case HMUX_HEADER:
778         len = (is.read() << 8) + is.read();
779     
780     int headerSize = _headerSize;
781     
782     CharBuffer key = _headerKeys[headerSize];
783     key.clear();
784     
785     CharBuffer valueCb = _headerValues[headerSize];
786     valueCb.clear();
787     
788     _rawRead.readAll(key, len);
789     code = _rawRead.read();
790     if (code != HMUX_STRING)
791       throw new IOException JavaDoc("protocol expected HMUX_STRING at " + (char) code);
792     _rawRead.readAll(valueCb, readLength());
793
794     addHeaderInt(key.getBuffer(), 0, key.length(), valueCb);
795
796     if (isLoggable)
797       log.fine(dbgId() + "H " + key + "=" + valueCb);
798
799     _headerSize++;
800     break;
801
802       case CSE_CONTENT_LENGTH:
803         len = (is.read() << 8) + is.read();
804     if (_headerKeys.length <= _headerSize)
805       resizeHeaders();
806     _headerKeys[_headerSize].clear();
807     _headerKeys[_headerSize].append("Content-Length");
808     _headerValues[_headerSize].clear();
809     _rawRead.readAll(_headerValues[_headerSize], len);
810
811     if (isLoggable)
812       log.fine(dbgId() + (char) code + " content-length=" +
813                    _headerValues[_headerSize]);
814     _headerSize++;
815     break;
816
817       case CSE_CONTENT_TYPE:
818         len = (is.read() << 8) + is.read();
819     if (_headerKeys.length <= _headerSize)
820       resizeHeaders();
821     _headerKeys[_headerSize].clear();
822     _headerKeys[_headerSize].append("Content-Type");
823     _headerValues[_headerSize].clear();
824     _rawRead.readAll(_headerValues[_headerSize], len);
825     if (isLoggable)
826       log.fine(dbgId() + (char) code + " content-type=" +
827                    _headerValues[_headerSize]);
828     _headerSize++;
829     break;
830
831       case CSE_IS_SECURE:
832         len = (is.read() << 8) + is.read();
833     _isSecure = true;
834     if (isLoggable)
835       log.fine(dbgId() + "secure");
836     _rawRead.skip(len);
837     break;
838
839       case CSE_CLIENT_CERT:
840         len = (is.read() << 8) + is.read();
841     _clientCert.clear();
842         _clientCert.setLength(len);
843     _rawRead.readAll(_clientCert.getBuffer(), 0, len);
844     if (isLoggable)
845       log.fine(dbgId() + (char) code + " cert=" + _clientCert +
846                    " len:" + len);
847     break;
848
849       case CSE_SERVER_TYPE:
850         len = (is.read() << 8) + is.read();
851     _cb1.clear();
852     _rawRead.readAll(_cb1, len);
853     if (isLoggable)
854       log.fine(dbgId() + (char) code + " server=" + _cb1);
855         if (_cb1.length() > 0)
856           _serverType = _cb1.charAt(0);
857     break;
858
859       case CSE_REMOTE_USER:
860         len = (is.read() << 8) + is.read();
861         _cb.clear();
862     _rawRead.readAll(_cb, len);
863     if (isLoggable)
864       log.fine(dbgId() + (char) code + " " + _cb);
865         setAttribute(com.caucho.server.security.AbstractAuthenticator.LOGIN_NAME,
866                      new com.caucho.security.BasicPrincipal(_cb.toString()));
867     break;
868     
869       case CSE_DATA:
870         len = (is.read() << 8) + is.read();
871     _pendingData = len;
872     if (isLoggable)
873       log.fine(dbgId() + (char) code + " post-data: " + len);
874     return hasURI;
875
876       default:
877         len = (is.read() << 8) + is.read();
878
879     if (isLoggable)
880       log.fine(dbgId() + (char) code + " " + len);
881     is.skip(len);
882     break;
883       }
884     }
885
886     // _filter.setClientClosed(true);
887

888     // return false;
889
}
890
891   private void resizeHeaders()
892   {
893     CharBuffer []newKeys = new CharBuffer[_headerSize * 2];
894     CharBuffer []newValues = new CharBuffer[_headerSize * 2];
895
896     for (int i = 0; i < _headerSize; i++) {
897       newKeys[i] = _headerKeys[i];
898       newValues[i] = _headerValues[i];
899     }
900     
901     for (int i = _headerSize; i < newKeys.length; i++) {
902       newKeys[i] = new CharBuffer();
903       newValues[i] = new CharBuffer();
904     }
905
906     _headerKeys = newKeys;
907     _headerValues = newValues;
908   }
909
910   private int readLength()
911     throws IOException JavaDoc
912   {
913     return ((_rawRead.read() << 8) + _rawRead.read());
914   }
915
916   /**
917    * Returns the header.
918    */

919   public String JavaDoc getMethod()
920   {
921     if (_methodString == null) {
922       CharSegment cb = getMethodBuffer();
923       if (cb.length() == 0) {
924         _methodString = "GET";
925         return _methodString;
926       }
927       
928       switch (cb.charAt(0)) {
929       case 'G':
930         _methodString = cb.equals(_getCb) ? "GET" : cb.toString();
931         break;
932         
933       case 'H':
934         _methodString = cb.equals(_headCb) ? "HEAD" : cb.toString();
935         break;
936         
937       case 'P':
938         _methodString = cb.equals(_postCb) ? "POST" : cb.toString();
939         break;
940
941       default:
942         _methodString = cb.toString();
943       }
944     }
945
946     return _methodString;
947     
948   }
949
950   public CharSegment getMethodBuffer()
951   {
952     return _method;
953   }
954
955   /**
956    * Returns a char buffer containing the host.
957    */

958   protected CharBuffer getHost()
959   {
960     if (_host.length() > 0)
961       return _host;
962     
963     _host.append(_serverName);
964     _host.toLowerCase();
965
966     return _host;
967   }
968
969   public final byte []getUriBuffer()
970   {
971     return _uri.getBuffer();
972   }
973
974   public final int getUriLength()
975   {
976     return _uri.getLength();
977   }
978
979   /**
980    * Returns the protocol.
981    */

982   public String JavaDoc getProtocol()
983   {
984     return _protocol.toString();
985   }
986
987   public CharSegment getProtocolBuffer()
988   {
989     return _protocol;
990   }
991
992   final int getVersion()
993   {
994     return _version;
995   }
996
997   /**
998    * Returns true if the request is secure.
999    */

1000  public boolean isSecure()
1001  {
1002    return _isSecure;
1003  }
1004
1005  /**
1006   * Returns the header.
1007   */

1008  public String JavaDoc getHeader(String JavaDoc key)
1009  {
1010    CharSegment buf = getHeaderBuffer(key);
1011    if (buf != null)
1012      return buf.toString();
1013    else
1014      return null;
1015  }
1016
1017  public CharSegment getHeaderBuffer(String JavaDoc key)
1018  {
1019    for (int i = 0; i < _headerSize; i++) {
1020      CharBuffer test = _headerKeys[i];
1021
1022      if (test.equalsIgnoreCase(key))
1023    return _headerValues[i];
1024    }
1025
1026    return null;
1027  }
1028
1029  public CharSegment getHeaderBuffer(char []buf, int length)
1030  {
1031    for (int i = 0; i < _headerSize; i++) {
1032      CharBuffer test = _headerKeys[i];
1033
1034      if (test.length() != length)
1035        continue;
1036      
1037      char []keyBuf = test.getBuffer();
1038      int j;
1039      for (j = 0; j < length; j++) {
1040        char a = buf[j];
1041        char b = keyBuf[j];
1042        if (a == b)
1043          continue;
1044
1045        if (a >= 'A' && a <= 'Z')
1046          a += 'a' - 'A';
1047        if (b >= 'A' && b <= 'Z')
1048          b += 'a' - 'A';
1049        if (a != b)
1050          break;
1051      }
1052
1053      if (j == length)
1054        return _headerValues[i];
1055    }
1056
1057    return null;
1058  }
1059
1060  public void setHeader(String JavaDoc key, String JavaDoc value)
1061  {
1062    if (_headerKeys.length <= _headerSize)
1063      resizeHeaders();
1064    
1065    _headerKeys[_headerSize].clear();
1066    _headerKeys[_headerSize].append(key);
1067    _headerValues[_headerSize].clear();
1068    _headerValues[_headerSize].append(value);
1069    _headerSize++;
1070  }
1071
1072  public void getHeaderBuffers(String JavaDoc key, ArrayList JavaDoc<CharSegment> values)
1073  {
1074    CharBuffer cb = _cb;
1075    
1076    cb.clear();
1077    cb.append(key);
1078
1079    int size = _headerSize;
1080    for (int i = 0; i < size; i++) {
1081      CharBuffer test = _headerKeys[i];
1082      if (test.equalsIgnoreCase(cb))
1083    values.add(_headerValues[i]);
1084    }
1085  }
1086
1087  public Enumeration JavaDoc getHeaderNames()
1088  {
1089    HashSet JavaDoc<String JavaDoc> names = new HashSet JavaDoc<String JavaDoc>();
1090    for (int i = 0; i < _headerSize; i++)
1091      names.add(_headerKeys[i].toString());
1092
1093    return Collections.enumeration(names);
1094  }
1095
1096  /**
1097   * Returns the URI for the request, special casing the IIS issues.
1098   * Because IIS already escapes the URI before sending it, the URI
1099   * needs to be re-escaped.
1100   */

1101  public String JavaDoc getRequestURI()
1102  {
1103    if (_serverType == 'R')
1104      return super.getRequestURI();
1105
1106    String JavaDoc _rawURI = super.getRequestURI();
1107    CharBuffer cb = CharBuffer.allocate();
1108
1109    for (int i = 0; i < _rawURI.length(); i++) {
1110      char ch = _rawURI.charAt(i);
1111
1112      switch (ch) {
1113      case ' ':
1114      case '?':
1115        addHex(cb, ch);
1116        break;
1117
1118      default:
1119        cb.append(ch);
1120        break;
1121      }
1122    }
1123
1124    return cb.close();
1125  }
1126
1127  /**
1128   * Adds a hex escape.
1129   *
1130   * @param cb the char buffer containing the escape.
1131   * @param ch the character to be escaped.
1132   */

1133  private void addHex(CharBuffer cb, int ch)
1134  {
1135    cb.append('%');
1136
1137    int d = (ch >> 4) & 0xf;
1138    if (d < 10)
1139      cb.append((char) ('0' + d));
1140    else
1141      cb.append((char) ('a' + d - 10));
1142    
1143    d = ch & 0xf;
1144    if (d < 10)
1145      cb.append((char) ('0' + d));
1146    else
1147      cb.append((char) ('a' + d - 10));
1148  }
1149
1150  /**
1151   * Returns the server name.
1152   */

1153  public String JavaDoc getServerName()
1154  {
1155    CharBuffer host = getHost();
1156    if (host == null) {
1157      InetAddress JavaDoc addr = getConnection().getRemoteAddress();
1158      return addr.getHostName();
1159    }
1160
1161    int p = host.indexOf(':');
1162    if (p >= 0)
1163      return host.substring(0, p);
1164    else
1165      return host.toString();
1166  }
1167
1168  public int getServerPort()
1169  {
1170    int len = _serverPort.length();
1171    int port = 0;
1172    for (int i = 0; i < len; i++) {
1173      char ch = _serverPort.charAt(i);
1174      port = 10 * port + ch - '0';
1175    }
1176
1177    return port;
1178  }
1179
1180  public String JavaDoc getRemoteAddr()
1181  {
1182    return _remoteAddr.toString();
1183  }
1184
1185  public void getRemoteAddr(CharBuffer cb)
1186  {
1187    cb.append(_remoteAddr);
1188  }
1189
1190  public int printRemoteAddr(byte []buffer, int offset)
1191    throws IOException JavaDoc
1192  {
1193    char []buf = _remoteAddr.getBuffer();
1194    int len = _remoteAddr.getLength();
1195
1196    for (int i = 0; i < len; i++)
1197      buffer[offset + i] = (byte) buf[i];
1198
1199    return offset + len;
1200  }
1201
1202  public String JavaDoc getRemoteHost()
1203  {
1204    return _remoteHost.toString();
1205  }
1206
1207  /**
1208   * Called for a connection: close
1209   */

1210  protected void connectionClose()
1211  {
1212    // ignore for hmux
1213
}
1214
1215  // Response data
1216
void writeStatus(CharBuffer message)
1217    throws IOException JavaDoc
1218  {
1219    int channel = 2;
1220
1221    WriteStream os = _rawWrite;
1222    
1223    os.write(HMUX_CHANNEL);
1224    os.write(channel >> 8);
1225    os.write(channel);
1226              
1227    writeString(HMUX_STATUS, message);
1228  }
1229
1230  /**
1231   * Complete sending of all headers.
1232   */

1233  void sendHeader()
1234    throws IOException JavaDoc
1235  {
1236    writeString(CSE_SEND_HEADER, "");
1237  }
1238
1239  /**
1240   * Writes a header to the plugin.
1241   *
1242   * @param key the header's key
1243   * @param value the header's value
1244   */

1245  void writeHeader(String JavaDoc key, String JavaDoc value)
1246    throws IOException JavaDoc
1247  {
1248    writeString(HMUX_HEADER, key);
1249    writeString(HMUX_STRING, value);
1250  }
1251
1252  /**
1253   * Writes a header to the plugin.
1254   *
1255   * @param key the header's key
1256   * @param value the header's value
1257   */

1258  void writeHeader(String JavaDoc key, CharBuffer value)
1259    throws IOException JavaDoc
1260  {
1261    writeString(HMUX_HEADER, key);
1262    writeString(HMUX_STRING, value);
1263  }
1264
1265  void writeString(int code, String JavaDoc value)
1266    throws IOException JavaDoc
1267  {
1268    int len = value.length();
1269
1270    WriteStream os = _rawWrite;
1271    
1272    os.write(code);
1273    os.write(len >> 8);
1274    os.write(len);
1275    os.print(value);
1276    
1277    if (log.isLoggable(Level.FINE))
1278      log.fine(dbgId() + (char)code + " " + value);
1279  }
1280
1281  void writeString(int code, CharBuffer cb)
1282    throws IOException JavaDoc
1283  {
1284    int len = cb.length();
1285
1286    WriteStream os = _rawWrite;
1287    
1288    os.write(code);
1289    os.write(len >> 8);
1290    os.write(len);
1291    os.print(cb.getBuffer(), 0, len);
1292
1293    if (log.isLoggable(Level.FINE))
1294      log.fine(dbgId() + (char)code + " " + cb);
1295  }
1296  
1297  public void protocolCloseEvent()
1298  {
1299  }
1300
1301  public final String JavaDoc dbgId()
1302  {
1303    String JavaDoc id = _server.getServerId();
1304
1305    if (id.equals(""))
1306      return "[" + getConnection().getId() + "] ";
1307    else
1308      return "[" + id + ":" + getConnection().getId() + "] ";
1309  }
1310
1311  public String JavaDoc toString()
1312  {
1313    return "HmuxRequest" + dbgId();
1314  }
1315
1316  /**
1317   * Implements the protocol for data reads and writes. Data from the
1318   * web server to the JVM must be acked, except for the first data.
1319   * Data back to the web server needs no ack.
1320   */

1321  static class ServletFilter extends StreamImpl {
1322    HmuxRequest _request;
1323    ReadStream _is;
1324    WriteStream _os;
1325    byte []_buffer = new byte[16];
1326    int _pendingData;
1327    boolean _needsAck;
1328    boolean _isClosed;
1329    boolean _isClientClosed;
1330
1331    ServletFilter()
1332    {
1333    }
1334
1335    void init(HmuxRequest request,
1336          ReadStream nextRead, WriteStream nextWrite)
1337    {
1338      _request = request;
1339      _is = nextRead;
1340      _os = nextWrite;
1341      _pendingData = 0;
1342      _isClosed = false;
1343      _isClientClosed = false;
1344      _needsAck = false;
1345    }
1346
1347    void setPending(int pendingData)
1348    {
1349      _pendingData = pendingData;
1350    }
1351
1352    void setClientClosed(boolean isClientClosed)
1353    {
1354      _isClientClosed = isClientClosed;
1355    }
1356
1357    public boolean canRead()
1358    {
1359      return true;
1360    }
1361
1362    public int getAvailable()
1363    {
1364      return _pendingData;
1365    }
1366
1367    /**
1368     * Reads available data. If the data needs an ack, then do so.
1369     */

1370    public int read(byte []buf, int offset, int length)
1371      throws IOException JavaDoc
1372    {
1373      int sublen = _pendingData;
1374      ReadStream is = _is;
1375      
1376      if (sublen <= 0)
1377    return -1;
1378
1379      if (length < sublen)
1380    sublen = length;
1381
1382      int readLen = is.read(buf, offset, sublen);
1383      _pendingData -= readLen;
1384
1385      if (log.isLoggable(Level.FINEST))
1386        log.finest(new String JavaDoc(buf, offset, readLen));
1387
1388      while (_pendingData == 0) {
1389        if (_needsAck) {
1390          int channel = 2;
1391          
1392          _os.write(HMUX_ACK);
1393          _os.write(channel >> 8);
1394          _os.write(channel);
1395          
1396          if (log.isLoggable(Level.FINE))
1397            log.fine(_request.dbgId() + "A:ack channel 2");
1398        }
1399        
1400        _needsAck = false;
1401
1402    int code = is.read();
1403
1404        if (code == HMUX_DATA) {
1405          int len = (is.read() << 8) + is.read();
1406          
1407          if (log.isLoggable(Level.FINE))
1408            log.fine(_request.dbgId() + "D:post-data " + len);
1409
1410          _pendingData = len;
1411        }
1412        else if (code == HMUX_QUIT) {
1413          if (log.isLoggable(Level.FINE))
1414            log.fine(_request.dbgId() + "Q:quit");
1415      
1416          return readLen;
1417        }
1418        else if (code == HMUX_EXIT) {
1419          if (log.isLoggable(Level.FINE))
1420            log.fine(_request.dbgId() + "X:exit");
1421      
1422      _request.killKeepalive();
1423          return readLen;
1424        }
1425        else if (code == HMUX_YIELD) {
1426          _needsAck = true;
1427        }
1428    else if (code == HMUX_CHANNEL) {
1429      int channel = (is.read() << 8) + is.read();
1430      
1431          if (log.isLoggable(Level.FINE))
1432            log.fine(_request.dbgId() + "channel " + channel);
1433    }
1434        else if (code < 0) {
1435      _request.killKeepalive();
1436    
1437          return readLen;
1438    }
1439        else {
1440      _request.killKeepalive();
1441      
1442          int len = (is.read() << 8) + is.read();
1443          
1444          if (log.isLoggable(Level.FINE))
1445            log.fine(_request.dbgId() + "unknown `" + (char) code + "' " + len);
1446
1447          is.skip(len);
1448        }
1449      }
1450
1451      return readLen;
1452    }
1453
1454    public boolean canWrite()
1455    {
1456      return true;
1457    }
1458
1459    /**
1460     * Send data back to the web server
1461     */

1462    public void write(byte []buf, int offset, int length, boolean isEnd)
1463      throws IOException JavaDoc
1464    {
1465      if (log.isLoggable(Level.FINE)) {
1466    log.fine(_request.dbgId() + (char) HMUX_DATA + ":data " + length);
1467        
1468        if (log.isLoggable(Level.FINEST))
1469          log.finest(_request.dbgId() + "data <" + new String JavaDoc(buf, offset, length) + ">");
1470      }
1471
1472      byte []tempBuf = _buffer;
1473      
1474      while (length > 0) {
1475    int sublen = length;
1476
1477    if (32 * 1024 < sublen)
1478      sublen = 32 * 1024;
1479    
1480        // The 3 bytes are already allocated by setPrefixWrite
1481
tempBuf[0] = HMUX_DATA;
1482    tempBuf[1] = (byte) (sublen >> 8);
1483    tempBuf[2] = (byte) sublen;
1484
1485    _os.write(tempBuf, 0, 3);
1486    _os.write(buf, offset, sublen);
1487
1488    length -= sublen;
1489    offset += sublen;
1490      }
1491    }
1492
1493    public void flush()
1494      throws IOException JavaDoc
1495    {
1496      if (log.isLoggable(Level.FINE))
1497    log.fine(_request.dbgId() + (char) HMUX_FLUSH + ":flush");
1498
1499      _os.write(HMUX_FLUSH);
1500      _os.write(0);
1501      _os.write(0);
1502      _os.flush();
1503    }
1504
1505    public void close()
1506      throws IOException JavaDoc
1507    {
1508      if (_isClosed)
1509    return;
1510
1511      _isClosed = true;
1512
1513      if (_pendingData > 0) {
1514    _is.skip(_pendingData);
1515    _pendingData = 0;
1516      }
1517
1518      boolean keepalive = _request.allowKeepalive();
1519
1520      if (! _isClientClosed) {
1521    if (log.isLoggable(Level.FINE)) {
1522          if (keepalive)
1523            log.fine(_request.dbgId() + (char) HMUX_QUIT + ": quit channel");
1524          else
1525            log.fine(_request.dbgId() + (char) HMUX_EXIT + ": exit socket");
1526        }
1527
1528        if (keepalive)
1529          _os.write(HMUX_QUIT);
1530        else
1531          _os.write(HMUX_EXIT);
1532      }
1533
1534      if (keepalive)
1535        _os.flush();
1536      else
1537        _os.close();
1538      //nextRead.close();
1539
}
1540  }
1541}
1542
Popular Tags