The Labs \ Source Viewer \ SSCLI \ System.Runtime.Remoting.Channels.Http \ HttpFixedLengthReadingStream

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. //============================================================
  16. //
  17. // File: HttpStreams.cs
  18. //
  19. // Summary: Defines streams used by HTTP channels
  20. //
  21. //============================================================
  22. using System;
  23. using System.Collections;
  24. using System.IO;
  25. using System.Net;
  26. using System.Net.Sockets;
  27. using System.Runtime.Remoting.Channels;
  28. using System.Text;
  29. using System.Threading;
  30. using System.Globalization;
  31. namespace System.Runtime.Remoting.Channels.Http
  32. {
  33.     internal abstract class HttpServerResponseStream : Stream
  34.     {
  35.         public override bool CanRead {
  36.             get { return false; }
  37.         }
  38.         public override bool CanSeek {
  39.             get { return false; }
  40.         }
  41.         public override bool CanWrite {
  42.             get { return true; }
  43.         }
  44.        
  45.         public override long Length {
  46.             get {
  47.                 throw new NotSupportedException();
  48.             }
  49.         }
  50.        
  51.         public override long Position {
  52.             get {
  53.                 throw new NotSupportedException();
  54.             }
  55.             set {
  56.                 throw new NotSupportedException();
  57.             }
  58.         }
  59.        
  60.         public override long Seek(long offset, SeekOrigin origin)
  61.         {
  62.             throw new NotSupportedException();
  63.         }
  64.         public override void SetLength(long value)
  65.         {
  66.             throw new NotSupportedException();
  67.         }
  68.        
  69.         public override int Read(byte[] buffer, int offset, int count)
  70.         {
  71.             throw new NotSupportedException();
  72.         }
  73.     }
  74.     // HttpServerResponseStream
  75.    
  76.     internal sealed class HttpFixedLengthResponseStream : HttpServerResponseStream
  77.     {
  78.         private Stream _outputStream = null;
  79.         // funnel http data into here
  80.         private static int _length;
  81.        
  82.         internal HttpFixedLengthResponseStream(Stream outputStream, int length)
  83.         {
  84.             _outputStream = outputStream;
  85.             _length = length;
  86.         }
  87.         // HttpFixedLengthResponseStream
  88.        
  89.         protected override void Dispose(bool disposing)
  90.         {
  91.             try {
  92.                 if (disposing)
  93.                     _outputStream.Flush();
  94.             }
  95.             finally {
  96.                 base.Dispose(disposing);
  97.             }
  98.         }
  99.         // Close
  100.         public override void Flush()
  101.         {
  102.             _outputStream.Flush();
  103.         }
  104.         // Flush
  105.         public override void Write(byte[] buffer, int offset, int count)
  106.         {
  107.             _outputStream.Write(buffer, offset, count);
  108.         }
  109.         // Write
  110.         public override void WriteByte(byte value)
  111.         {
  112.             _outputStream.WriteByte(value);
  113.         }
  114.         // WriteByte
  115.     }
  116.     // class HttpFixedLengthResponseStream
  117.    
  118.     internal sealed class HttpChunkedResponseStream : HttpServerResponseStream
  119.     {
  120.         private static byte[] _trailer = Encoding.ASCII.GetBytes("0\r\n\r\n");
  121.         // 0-length, no trailer, end chunked
  122.         private static byte[] _endChunk = Encoding.ASCII.GetBytes("\r\n");
  123.        
  124.         private Stream _outputStream = null;
  125.         // funnel chunked http data into here
  126.         private byte[] _chunk;
  127.         // chunk of data to write
  128.         private int _chunkSize;
  129.         // size of chunk
  130.         private int _chunkOffset;
  131.         // next byte to write in to chunk
  132.         private byte[] _byteBuffer = new byte[1];
  133.         // buffer for writing bytes
  134.        
  135.         internal HttpChunkedResponseStream(Stream outputStream)
  136.         {
  137.             _outputStream = outputStream;
  138.            
  139.             _chunk = CoreChannel.BufferPool.GetBuffer();
  140.             _chunkSize = _chunk.Length - 2;
  141.             // reserve space for _endChunk directly in buffer
  142.             _chunkOffset = 0;
  143.            
  144.             // write end chunk bytes at end of buffer (avoids extra socket write)
  145.             _chunk[_chunkSize - 2] = (byte)'\r';
  146.             _chunk[_chunkSize - 1] = (byte)'\n';
  147.         }
  148.         // HttpChunkedResponseStream
  149.        
  150.         protected override void Dispose(bool disposing)
  151.         {
  152.             try {
  153.                 if (disposing) {
  154.                     if (_chunkOffset > 0)
  155.                         FlushChunk();
  156.                    
  157.                     _outputStream.Write(_trailer, 0, _trailer.Length);
  158.                     _outputStream.Flush();
  159.                 }
  160.                
  161.                 CoreChannel.BufferPool.ReturnBuffer(_chunk);
  162.                 _chunk = null;
  163.             }
  164.             finally {
  165.                 base.Dispose(disposing);
  166.             }
  167.         }
  168.         // Close
  169.         public override void Flush()
  170.         {
  171.             if (_chunkOffset > 0)
  172.                 FlushChunk();
  173.             _outputStream.Flush();
  174.         }
  175.         // Flush
  176.         public override void Write(byte[] buffer, int offset, int count)
  177.         {
  178.             while (count > 0) {
  179.                 if ((_chunkOffset == 0) && (count >= _chunkSize)) {
  180.                     // just write the rest as a chunk directly to the wire
  181.                     WriteChunk(buffer, offset, count);
  182.                     break;
  183.                 }
  184.                 else {
  185.                     // write bytes to current chunk buffer
  186.                     int writeCount = Math.Min(_chunkSize - _chunkOffset, count);
  187.                     Array.Copy(buffer, offset, _chunk, _chunkOffset, writeCount);
  188.                     _chunkOffset += writeCount;
  189.                     count -= writeCount;
  190.                     offset += writeCount;
  191.                    
  192.                     // see if we need to terminate the chunk
  193.                     if (_chunkOffset == _chunkSize)
  194.                         FlushChunk();
  195.                 }
  196.             }
  197.         }
  198.         // Write
  199.         public override void WriteByte(byte value)
  200.         {
  201.             _byteBuffer[0] = value;
  202.             Write(_byteBuffer, 0, 1);
  203.         }
  204.         // WriteByte
  205.         private void FlushChunk()
  206.         {
  207.             WriteChunk(_chunk, 0, _chunkOffset);
  208.             _chunkOffset = 0;
  209.         }
  210.        
  211.         private void WriteChunk(byte[] buffer, int offset, int count)
  212.         {
  213.             byte[] size = IntToHexChars(count);
  214.            
  215.             _outputStream.Write(size, 0, size.Length);
  216.            
  217.             if (buffer == _chunk) {
  218.                 // _chunk already has end chunk encoding at end
  219.                 _outputStream.Write(_chunk, offset, count + 2);
  220.             }
  221.             else {
  222.                 _outputStream.Write(buffer, offset, count);
  223.                 _outputStream.Write(_endChunk, 0, _endChunk.Length);
  224.             }
  225.         }
  226.         // WriteChunk
  227.        
  228.         private byte[] IntToHexChars(int i)
  229.         {
  230.             string str = "";
  231.            
  232.             while (i > 0) {
  233.                 int val = i % 16;
  234.                
  235.                 switch (val) {
  236.                     case 15:
  237.                         str = 'F' + str;
  238.                         break;
  239.                     case 14:
  240.                         str = 'E' + str;
  241.                         break;
  242.                     case 13:
  243.                         str = 'D' + str;
  244.                         break;
  245.                     case 12:
  246.                         str = 'C' + str;
  247.                         break;
  248.                     case 11:
  249.                         str = 'B' + str;
  250.                         break;
  251.                     case 10:
  252.                         str = 'A' + str;
  253.                         break;
  254.                     default:
  255.                        
  256.                         str = (char)(val + (int)'0') + str;
  257.                         break;
  258.                 }
  259.                
  260.                 i = i / 16;
  261.             }
  262.            
  263.             str += "\r\n";
  264.            
  265.             return Encoding.ASCII.GetBytes(str);
  266.         }
  267.         // IntToHexChars
  268.     }
  269.     // HttpChunkedResponseStream
  270.    
  271.    
  272.     internal abstract class HttpReadingStream : Stream
  273.     {
  274.         public virtual bool ReadToEnd()
  275.         {
  276.             // This will never be called at a point where it is valid
  277.             // for someone to use the remaining data, so we don't
  278.             // need to buffer it.
  279.            
  280.             byte[] buffer = new byte[16];
  281.             int bytesRead = 0;
  282.             do {
  283.                 bytesRead = Read(buffer, 0, 16);
  284.             }
  285.             while (bytesRead > 0);
  286.            
  287.             return bytesRead == 0;
  288.         }
  289.        
  290.        
  291.         public virtual bool FoundEnd {
  292.             get { return false; }
  293.         }
  294.        
  295.         public override bool CanRead {
  296.             get { return true; }
  297.         }
  298.         public override bool CanSeek {
  299.             get { return false; }
  300.         }
  301.         public override bool CanWrite {
  302.             get { return false; }
  303.         }
  304.        
  305.         public override long Length {
  306.             get {
  307.                 throw new NotSupportedException();
  308.             }
  309.         }
  310.        
  311.         public override long Position {
  312.             get {
  313.                 throw new NotSupportedException();
  314.             }
  315.             set {
  316.                 throw new NotSupportedException();
  317.             }
  318.         }
  319.        
  320.         public override void Flush()
  321.         {
  322.             throw new NotSupportedException();
  323.         }
  324.        
  325.         public override long Seek(long offset, SeekOrigin origin)
  326.         {
  327.             throw new NotSupportedException();
  328.         }
  329.         public override void SetLength(long value)
  330.         {
  331.             throw new NotSupportedException();
  332.         }
  333.        
  334.         public override void Write(byte[] buffer, int offset, int count)
  335.         {
  336.             throw new NotSupportedException();
  337.         }
  338.        
  339.     }
  340.     // HttpReadingStream
  341.    
  342.     internal sealed class HttpFixedLengthReadingStream : HttpReadingStream
  343.     {
  344.         private HttpSocketHandler _inputStream = null;
  345.         // read content data from here
  346.         private int _bytesLeft;
  347.         // bytes left in current chunk
  348.         internal HttpFixedLengthReadingStream(HttpSocketHandler inputStream, int contentLength)
  349.         {
  350.             _inputStream = inputStream;
  351.             _bytesLeft = contentLength;
  352.         }
  353.         // HttpFixedLengthReadingStream
  354.        
  355.         public override bool FoundEnd {
  356.             get { return _bytesLeft == 0; }
  357.         }
  358.        
  359.         protected override void Dispose(bool disposing)
  360.         {
  361.             try {
  362.             }
  363.             finally {
  364.                 base.Dispose(disposing);
  365.             }
  366.         }
  367.        
  368.         public override int Read(byte[] buffer, int offset, int count)
  369.         {
  370.             if (_bytesLeft == 0)
  371.                 return 0;
  372.            
  373.             int readCount = _inputStream.Read(buffer, offset, Math.Min(_bytesLeft, count));
  374.             if (readCount > 0)
  375.                 _bytesLeft -= readCount;
  376.            
  377.             return readCount;
  378.         }
  379.         // Read
  380.         public override int ReadByte()
  381.         {
  382.             if (_bytesLeft == 0)
  383.                 return -1;
  384.            
  385.             _bytesLeft -= 1;
  386.             return _inputStream.ReadByte();
  387.         }
  388.         // ReadByte
  389.     }
  390.     // HttpFixedLengthReadingStream
  391.    
  392.    
  393.     // Stream class to read chunked data for HTTP
  394.     // (assumes that provided outputStream will be positioned for
  395.     // reading the body)
  396.     internal sealed class HttpChunkedReadingStream : HttpReadingStream
  397.     {
  398.         private static byte[] _trailer = Encoding.ASCII.GetBytes("0\r\n\r\n\r\n");
  399.         // 0-length, null trailer, end chunked
  400.         private static byte[] _endChunk = Encoding.ASCII.GetBytes("\r\n");
  401.        
  402.         private HttpSocketHandler _inputStream = null;
  403.         // read chunked http data from here
  404.         private int _bytesLeft;
  405.         // bytes left in current chunk
  406.         private bool _bFoundEnd = false;
  407.         // has end of stream been reached?
  408.         private byte[] _byteBuffer = new byte[1];
  409.         // buffer for reading bytes
  410.        
  411.         internal HttpChunkedReadingStream(HttpSocketHandler inputStream)
  412.         {
  413.             _inputStream = inputStream;
  414.            
  415.             _bytesLeft = 0;
  416.         }
  417.         // HttpChunkedReadingStream
  418.         public override bool FoundEnd {
  419.             get { return _bFoundEnd; }
  420.         }
  421.        
  422.         protected override void Dispose(bool disposing)
  423.         {
  424.             try {
  425.             }
  426.             finally {
  427.                 base.Dispose(disposing);
  428.             }
  429.         }
  430.         // Close
  431.         public override int Read(byte[] buffer, int offset, int count)
  432.         {
  433.             int bytesRead = 0;
  434.            
  435.             while (!_bFoundEnd && (count > 0)) {
  436.                 // see if we need to start reading a new chunk
  437.                 if (_bytesLeft == 0) {
  438.                     // this loop stops when the end of line is found
  439.                     for (;;) {
  440.                         byte b = (byte)_inputStream.ReadByte();
  441.                        
  442.                         // see if this is the end of the length
  443.                         if (b == '\r') {
  444.                             // This had better be '\n'
  445.                             if ((char)_inputStream.ReadByte() != '\n') {
  446.                                 throw new RemotingException(CoreChannel.GetResourceString("Remoting_Http_ChunkedEncodingError"));
  447.                             }
  448.                             else
  449.                                 break;
  450.                             // we've finished reading the length
  451.                         }
  452.                         else {
  453.                             int value = HttpChannelHelper.CharacterHexDigitToDecimal(b);
  454.                             // make sure value is a hex-digit
  455.                             if ((value < 0) || (value > 15)) {
  456.                                 throw new RemotingException(CoreChannel.GetResourceString("Remoting_Http_ChunkedEncodingError"));
  457.                             }
  458.                            
  459.                             // update _bytesLeft value to account for new digit on the right
  460.                             _bytesLeft = (_bytesLeft * 16) + value;
  461.                         }
  462.                     }
  463.                    
  464.                     if (_bytesLeft == 0) {
  465.                         // read off trailing headers and end-line
  466.                         string trailerHeader;
  467.                         do {
  468.                             trailerHeader = _inputStream.ReadToEndOfLine();
  469.                         }
  470.                         while (!(trailerHeader.Length == 0));
  471.                        
  472.                         _bFoundEnd = true;
  473.                     }
  474.                 }
  475.                
  476.                 if (!_bFoundEnd) {
  477.                     int readCount = Math.Min(_bytesLeft, count);
  478.                     int bytesReadThisTime = _inputStream.Read(buffer, offset, readCount);
  479.                     if (bytesReadThisTime <= 0) {
  480.                         throw new RemotingException(CoreChannel.GetResourceString("Remoting_Http_ChunkedEncodingError"));
  481.                     }
  482.                    
  483.                     _bytesLeft -= bytesReadThisTime;
  484.                     count -= bytesReadThisTime;
  485.                     offset += bytesReadThisTime;
  486.                     bytesRead += bytesReadThisTime;
  487.                    
  488.                     // see if the end of the chunk was found
  489.                     if (_bytesLeft == 0) {
  490.                         // read off "\r\n"
  491.                         char ch = (char)_inputStream.ReadByte();
  492.                         if (ch != '\r') {
  493.                             throw new RemotingException(CoreChannel.GetResourceString("Remoting_Http_ChunkedEncodingError"));
  494.                         }
  495.                         ch = (char)_inputStream.ReadByte();
  496.                         if (ch != '\n') {
  497.                             throw new RemotingException(CoreChannel.GetResourceString("Remoting_Http_ChunkedEncodingError"));
  498.                         }
  499.                     }
  500.                 }
  501.             }
  502.             // while (count > 0)
  503.             return bytesRead;
  504.         }
  505.         // Read
  506.         public override int ReadByte()
  507.         {
  508.             int readCount = Read(_byteBuffer, 0, 1);
  509.             if (readCount == 0)
  510.                 return -1;
  511.            
  512.             return _byteBuffer[0];
  513.         }
  514.         // ReadByte
  515.     }
  516.     // class HttpChunkedReadingStream
  517.    
  518.     [Serializable()]
  519.     internal enum HttpVersion
  520.     {
  521.         V1_0,
  522.         V1_1
  523.     }
  524.     // HttpVersion
  525.    
  526.     // Maintains control of a socket connection.
  527.     internal sealed class HttpServerSocketHandler : HttpSocketHandler
  528.     {
  529.         // Used to make sure verb characters are valid
  530.         private static ValidateByteDelegate s_validateVerbDelegate = new ValidateByteDelegate(HttpServerSocketHandler.ValidateVerbCharacter);
  531.        
  532.         // Used to keep track of socket connections
  533.         private static Int64 _connectionIdCounter = 0;
  534.        
  535.         // primed buffer data
  536.         private static byte[] _bufferhttpContinue = Encoding.ASCII.GetBytes("HTTP/1.1 100 Continue\r\n\r\n");
  537.        
  538.         // stream manager data
  539.         private HttpReadingStream _requestStream = null;
  540.         // request stream we handed out.
  541.         private HttpServerResponseStream _responseStream = null;
  542.         // response stream we handed out.
  543.         private Int64 _connectionId;
  544.         // id for this connection
  545.         // request state flags
  546.         private HttpVersion _version;
  547.         // http version used by client
  548.         private int _contentLength = 0;
  549.         // Content-Length value if found
  550.         private bool _chunkedEncoding = false;
  551.         // does request stream use chunked encoding?
  552.         private bool _keepAlive = false;
  553.         // does the client want to keep the connection alive?
  554.        
  555.         internal HttpServerSocketHandler(Socket socket, RequestQueue requestQueue, Stream stream) : base(socket, requestQueue, stream)
  556.         {
  557.             _connectionId = Interlocked.Increment(ref _connectionIdCounter);
  558.         }
  559.         // HttpServerSocketHandler
  560.        
  561.         // FUTURE: Implement a response stream class that will only switch into chunked
  562.         // mode once was the response grows beyond a certain size. Chunking is
  563.         // disabled for now since without the custom stream, it degrades performance
  564.         // for small calls where chunking is not needed.
  565.         public bool AllowChunkedResponse {
  566.             get { return false; }
  567.         }
  568.        
  569.        
  570.         // Determine if it's possible to service another request
  571.         public bool CanServiceAnotherRequest()
  572.         {
  573.             if (_keepAlive && (_requestStream != null)) {
  574.                 if (_requestStream.FoundEnd || _requestStream.ReadToEnd())
  575.                     return true;
  576.             }
  577.            
  578.             return false;
  579.         }
  580.         // CanServiceAnotherRequest
  581.        
  582.         // Prepare for reading a new request off of the same socket
  583.         protected override void PrepareForNewMessage()
  584.         {
  585.             _requestStream = null;
  586.             _responseStream = null;
  587.            
  588.             _contentLength = 0;
  589.             _chunkedEncoding = false;
  590.             _keepAlive = false;
  591.         }
  592.         // PrepareForNewRequest
  593.         string GenerateFaultString(Exception e)
  594.         {
  595.             //If the user has specified it's a development server (versus a production server) in remoting config,
  596.             //then we should just return e.ToString instead of extracting the list of messages.
  597.             if (!CustomErrorsEnabled())
  598.                 return e.ToString();
  599.             else {
  600.                 return CoreChannel.GetResourceString("Remoting_InternalError");
  601.             }
  602.         }
  603.        
  604.         protected override void SendErrorMessageIfPossible(Exception e)
  605.         {
  606.             // If we haven't started sending a response back, we can do the following.
  607.             if ((_responseStream == null) && !(e is SocketException)) {
  608.                 Stream outputStream = new MemoryStream();
  609.                 StreamWriter writer = new StreamWriter(outputStream, new UTF8Encoding(false));
  610.                 writer.WriteLine(GenerateFaultString(e));
  611.                 writer.Flush();
  612.                
  613.                 SendResponse(outputStream, "500", CoreChannel.GetResourceString("Remoting_InternalError"), null);
  614.             }
  615.         }
  616.         // SendErrorMessageIfPossible
  617.        
  618.         private static bool ValidateVerbCharacter(byte b)
  619.         {
  620.             if (Char.IsLetter((char)b) || (b == '-')) {
  621.                 return true;
  622.             }
  623.            
  624.             return false;
  625.         }
  626.         // ValidateVerbCharacter
  627.        
  628.         // read headers
  629.         public BaseTransportHeaders ReadHeaders()
  630.         {
  631.             bool bSendContinue = false;
  632.            
  633.             BaseTransportHeaders headers = new BaseTransportHeaders();
  634.            
  635.             // read first line
  636.             string verb;
  637.             string requestURI;
  638.             string version;
  639.             ReadFirstLine(out verb, out requestURI, out version);
  640.            
  641.             if ((verb == null) || (requestURI == null) || (version == null)) {
  642.                 throw new RemotingException(CoreChannel.GetResourceString("Remoting_Http_UnableToReadFirstLine"));
  643.             }
  644.            
  645.             if (version.Equals("HTTP/1.1"))
  646.                 _version = HttpVersion.V1_1;
  647.             else if (version.Equals("HTTP/1.0"))
  648.                 _version = HttpVersion.V1_0;
  649.             else
  650.                 // most common case
  651.                 _version = HttpVersion.V1_1;
  652.             // (assume it will understand 1.1)
  653.             if (_version == HttpVersion.V1_1) {
  654.                 _keepAlive = true;
  655.             }
  656.             // it's a 1.0 client
  657.             else {
  658.                 _keepAlive = false;
  659.             }
  660.            
  661.            
  662.             // update request uri to be sure that it has no channel data
  663.             string channelURI;
  664.             string objectURI;
  665.             channelURI = HttpChannelHelper.ParseURL(requestURI, out objectURI);
  666.             if (channelURI == null) {
  667.                 objectURI = requestURI;
  668.             }
  669.            
  670.             headers["__RequestVerb"] = verb;
  671.             headers.RequestUri = objectURI;
  672.             headers["__HttpVersion"] = version;
  673.            
  674.             // check to see if we must send continue
  675.             if ((_version == HttpVersion.V1_1) && (verb.Equals("POST") || verb.Equals("PUT"))) {
  676.                 bSendContinue = true;
  677.             }
  678.            
  679.             ReadToEndOfHeaders(headers, out _chunkedEncoding, out _contentLength, ref _keepAlive, ref bSendContinue);
  680.            
  681.             if (bSendContinue && (_version != HttpVersion.V1_0))
  682.                 SendContinue();
  683.            
  684.             // add IP address and Connection Id to headers
  685.             headers[CommonTransportKeys.IPAddress] = ((IPEndPoint)NetSocket.RemoteEndPoint).Address;
  686.             headers[CommonTransportKeys.ConnectionId] = _connectionId;
  687.            
  688.             return headers;
  689.         }
  690.         // ReadHeaders
  691.        
  692.         public Stream GetRequestStream()
  693.         {
  694.             if (_chunkedEncoding)
  695.                 _requestStream = new HttpChunkedReadingStream(this);
  696.             else
  697.                 _requestStream = new HttpFixedLengthReadingStream(this, _contentLength);
  698.             return _requestStream;
  699.         }
  700.         // GetRequestStream
  701.        
  702.         public Stream GetResponseStream(string statusCode, string reasonPhrase, ITransportHeaders headers)
  703.         {
  704.             bool contentLengthPresent = false;
  705.             bool useChunkedEncoding = false;
  706.             int contentLength = 0;
  707.            
  708.             // check for custom user status code and reason phrase
  709.             object userStatusCode = headers["__HttpStatusCode"];
  710.             // someone might have stored an int
  711.             string userReasonPhrase = headers["__HttpReasonPhrase"] as string;
  712.            
  713.             if (userStatusCode != null)
  714.                 statusCode = userStatusCode.ToString();
  715.             if (userReasonPhrase != null)
  716.                 reasonPhrase = userReasonPhrase;
  717.            
  718.             // see if we can handle any more requests on this socket
  719.             if (!CanServiceAnotherRequest()) {
  720.                 headers["Connection"] = "Close";
  721.             }
  722.            
  723.             // check for content length
  724.             object contentLengthEntry = headers["Content-Length"];
  725.             if (contentLengthEntry != null) {
  726.                 contentLengthPresent = true;
  727.                 if (contentLengthEntry is int)
  728.                     contentLength = (int)contentLengthEntry;
  729.                 else
  730.                     contentLength = Convert.ToInt32(contentLengthEntry, CultureInfo.InvariantCulture);
  731.             }
  732.            
  733.             // see if we are going to use chunked-encoding
  734.             useChunkedEncoding = AllowChunkedResponse && !contentLengthPresent;
  735.             if (useChunkedEncoding)
  736.                 headers["Transfer-Encoding"] = "chunked";
  737.            
  738.            
  739.             // write headers to stream
  740.             ChunkedMemoryStream headerStream = new ChunkedMemoryStream(CoreChannel.BufferPool);
  741.             WriteResponseFirstLine(statusCode, reasonPhrase, headerStream);
  742.             WriteHeaders(headers, headerStream);
  743.            
  744.             headerStream.WriteTo(NetStream);
  745.             headerStream.Close();
  746.            
  747.            
  748.             // return stream ready for content
  749.             if (useChunkedEncoding)
  750.                 _responseStream = new HttpChunkedResponseStream(NetStream);
  751.             else
  752.                 _responseStream = new HttpFixedLengthResponseStream(NetStream, contentLength);
  753.            
  754.             return _responseStream;
  755.         }
  756.         // GetResponseStream
  757.        
  758.         private bool ReadFirstLine(out string verb, out string requestURI, out string version)
  759.         {
  760.             verb = null;
  761.             requestURI = null;
  762.             version = null;
  763.            
  764.             verb = ReadToChar(' ', s_validateVerbDelegate);
  765.            
  766.             byte[] requestUriBytes = ReadToByte((byte)' ');
  767.             int decodedUriLength;
  768.             HttpChannelHelper.DecodeUriInPlace(requestUriBytes, out decodedUriLength);
  769.             requestURI = Encoding.UTF8.GetString(requestUriBytes, 0, decodedUriLength);
  770.            
  771.             version = ReadToEndOfLine();
  772.            
  773.             return true;
  774.         }
  775.         // ReadFirstLine
  776.        
  777.         private void SendContinue()
  778.         {
  779.             // Output:
  780.             // HTTP/1.1 100 Continue
  781.            
  782.             // Send the continue response back to the client
  783.             NetStream.Write(_bufferhttpContinue, 0, _bufferhttpContinue.Length);
  784.         }
  785.         // SendContinue
  786.        
  787.         public void SendResponse(Stream httpContentStream, string statusCode, string reasonPhrase, ITransportHeaders headers)
  788.         {
  789.             if (_responseStream != null) {
  790.                 _responseStream.Close();
  791.                 if (_responseStream != httpContentStream) {
  792.                     throw new RemotingException(CoreChannel.GetResourceString("Remoting_Http_WrongResponseStream"));
  793.                 }
  794.                
  795.                 // we are done with the response stream
  796.                 _responseStream = null;
  797.             }
  798.             else {
  799.                 if (headers == null)
  800.                     headers = new TransportHeaders();
  801.                
  802.                 string serverHeader = (string)headers["Server"];
  803.                 if (serverHeader != null)
  804.                     serverHeader = HttpServerTransportSink.ServerHeader + ", " + serverHeader;
  805.                 else
  806.                     serverHeader = HttpServerTransportSink.ServerHeader;
  807.                 headers["Server"] = serverHeader;
  808.                
  809.                 // Add length to response headers if necessary
  810.                 if (!AllowChunkedResponse && (httpContentStream != null))
  811.                     headers["Content-Length"] = httpContentStream.Length.ToString(CultureInfo.InvariantCulture);
  812.                 else if (httpContentStream == null)
  813.                     headers["Content-Length"] = "0";
  814.                
  815.                 GetResponseStream(statusCode, reasonPhrase, headers);
  816.                
  817.                 // write HTTP content
  818.                 if (httpContentStream != null) {
  819.                     StreamHelper.CopyStream(httpContentStream, _responseStream);
  820.                    
  821.                     _responseStream.Close();
  822.                     httpContentStream.Close();
  823.                 }
  824.                
  825.                 // we are done with the response stream
  826.                 _responseStream = null;
  827.             }
  828.         }
  829.         // SendResponse
  830.        
  831.        
  832.     }
  833.     // class HttpServerSocketHandler
  834.    
  835.    
  836. }
  837. // namespace System.Runtime.Remoting.Channels.Http

Developer Fusion