HttpStreams.cs

  1. // ==++==
  2. // 
  3. //   
  4. //    Copyright (c) 2002 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. namespace System.Runtime.Remoting.Channels.Http
  31. {
  32.     internal abstract class HttpServerResponseStream : Stream
  33.     {
  34.         public override bool CanRead { get { return false; } }
  35.         public override bool CanSeek { get { return false; } }
  36.         public override bool CanWrite { get { return true; } }
  37.         
  38.         public override long Length  { get { throw new NotSupportedException(); } }
  39.         public override long Position
  40.         {
  41.              get { throw new NotSupportedException(); }
  42.              set { throw new NotSupportedException(); }
  43.         }
  44.         public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
  45.         public override void SetLength(long value) { throw new NotSupportedException(); }
  46.         
  47.         public override int Read(byte[] buffer, int offset, int count)
  48.         {
  49.             throw new NotSupportedException();
  50.         }
  51.     } // HttpServerResponseStream
  52.     internal class HttpFixedLengthResponseStream : HttpServerResponseStream
  53.     {
  54.         private Stream _outputStream = null// funnel http data into here
  55.         private static int _length;
  56.     
  57.         internal HttpFixedLengthResponseStream(Stream outputStream, int length)
  58.         {
  59.             _outputStream = outputStream;
  60.             _length = length;
  61.         } // HttpFixedLengthResponseStream
  62.         public override void Close() 
  63.         {            
  64.             _outputStream.Flush();
  65.         } // Close
  66.         public override void Flush()
  67.         {
  68.             _outputStream.Flush();
  69.         } // Flush
  70.         private int min(int a, int b) { return a < b ? a : b;}
  71.         
  72.         public override void Write(byte[] buffer, int offset, int count)
  73.         {            
  74.             _outputStream.Write(buffer, offset, count);           
  75.         } // Write
  76.         public override void WriteByte(byte value)
  77.         {
  78.             _outputStream.WriteByte(value);
  79.         } // WriteByte
  80.     
  81.     } // class HttpFixedLengthResponseStream
  82.     
  83.     internal class HttpChunkedResponseStream : HttpServerResponseStream
  84.     {
  85.         private static byte[] _trailer = Encoding.ASCII.GetBytes("0/r/n/r/n"); // 0-length, no trailer, end chunked
  86.         private static byte[] _endChunk = Encoding.ASCII.GetBytes("/r/n");
  87.    
  88.         private Stream _outputStream = null// funnel chunked http data into here
  89.         private byte[] _chunk;       // chunk of data to write
  90.         private int    _chunkSize;   // size of chunk
  91.         private int    _chunkOffset; // next byte to write in to chunk
  92.         private byte[] _byteBuffer = new byte[1]; // buffer for writing bytes
  93.         
  94.         internal HttpChunkedResponseStream(Stream outputStream)
  95.         {
  96.             _outputStream = outputStream;
  97.             _chunk = CoreChannel.BufferPool.GetBuffer();
  98.             _chunkSize = _chunk.Length - 2; // reserve space for _endChunk directly in buffer
  99.             _chunkOffset = 0;
  100.             // write end chunk bytes at end of buffer (avoids extra socket write)
  101.             _chunk[_chunkSize - 2] = (byte)'/r';
  102.             _chunk[_chunkSize - 1] = (byte)'/n';
  103.         } // HttpChunkedResponseStream
  104.         
  105.         public override void Close() 
  106.         {
  107.             if (_chunkOffset > 0)
  108.                 FlushChunk();            
  109.         
  110.             _outputStream.Write(_trailer, 0, _trailer.Length);
  111.             _outputStream.Flush();
  112.             CoreChannel.BufferPool.ReturnBuffer(_chunk);
  113.             _chunk = null;
  114.         } // Close
  115.         public override void Flush()
  116.         {
  117.             if (_chunkOffset > 0)
  118.                 FlushChunk();
  119.             _outputStream.Flush();
  120.         } // Flush
  121.         private int min(int a, int b) { return a < b ? a : b;}
  122.         
  123.         public override void Write(byte[] buffer, int offset, int count)
  124.         {
  125.             while (count > 0)
  126.             {
  127.                 if ((_chunkOffset == 0) && (count >= _chunkSize))
  128.                 {
  129.                     // just write the rest as a chunk directly to the wire
  130.                     WriteChunk(buffer, offset, count);
  131.                     break;
  132.                 }
  133.                 else
  134.                 {
  135.                     // write bytes to current chunk buffer
  136.                     int writeCount = min(_chunkSize - _chunkOffset, count);
  137.                     Array.Copy(buffer, offset, _chunk, _chunkOffset, writeCount);
  138.                     _chunkOffset += writeCount;
  139.                     count -= writeCount;
  140.                     offset += writeCount;
  141.                     // see if we need to terminate the chunk
  142.                     if (_chunkOffset == _chunkSize)
  143.                         FlushChunk();
  144.                 }
  145.             }
  146.         } // Write
  147.         public override void WriteByte(byte value)
  148.         {
  149.             _byteBuffer[0] = value;
  150.             Write(_byteBuffer, 0, 1);
  151.         } // WriteByte
  152.         private void FlushChunk()
  153.         {
  154.             WriteChunk(_chunk, 0, _chunkOffset);
  155.             _chunkOffset = 0;
  156.         }
  157.         private void WriteChunk(byte[] buffer, int offset, int count)
  158.         {
  159.             byte[] size = IntToHexChars(count);
  160.             _outputStream.Write(size, 0, size.Length);
  161.             if (buffer == _chunk)
  162.             {
  163.                 // _chunk already has end chunk encoding at end
  164.                 _outputStream.Write(_chunk, offset, count + 2);
  165.             }
  166.             else
  167.             {
  168.                 _outputStream.Write(buffer, offset, count);
  169.                 _outputStream.Write(_endChunk, 0, _endChunk.Length);
  170.             }
  171.         } // WriteChunk
  172.         
  173.         
  174.         private byte[] IntToHexChars(int i)
  175.         {
  176.             String str = "";
  177.             while (i > 0)
  178.             {
  179.                 int val = i % 16;
  180.                 switch (val)
  181.                 {
  182.                     case 15: str = 'F' + str; break;
  183.                     case 14: str = 'E' + str; break;
  184.                     case 13: str = 'D' + str; break;
  185.                     case 12: str = 'C' + str; break;
  186.                     case 11: str = 'B' + str; break;
  187.                     case 10: str = 'A' + str; break;
  188.                     default: str = (char)(val + (int)'0') + str; break;
  189.                 }
  190.                 i = i / 16;
  191.             }
  192.             str += "/r/n";
  193.              
  194.             return Encoding.ASCII.GetBytes(str);
  195.         } // IntToHexChars
  196.         
  197.     } // HttpChunkedResponseStream
  198.     
  199.     
  200.     internal abstract class HttpReadingStream : Stream
  201.     {
  202.         public virtual bool ReadToEnd()
  203.         {
  204.             // This will never be called at a point where it is valid
  205.             //   for someone to use the remaining data, so we don't
  206.             //   need to buffer it.
  207.             
  208.             byte[] buffer = new byte[16];
  209.             int bytesRead = 0;
  210.             do
  211.             {
  212.                bytesRead = Read(buffer, 0, 16);
  213.             } while (bytesRead > 0);
  214.             return bytesRead == 0;
  215.         }  
  216.         public virtual bool FoundEnd { get { return false; } }
  217.         
  218.         public override bool CanRead {  get { return true; } }
  219.         public override bool CanSeek { get { return false; } }
  220.         public override bool CanWrite { get { return false; } }
  221.        
  222.         public override long Length {  get { throw new NotSupportedException(); } }
  223.         public override long Position
  224.         {
  225.              getthrow new NotSupportedException(); }
  226.              setthrow new NotSupportedException(); }
  227.         }
  228.         public override  void Flush() { throw new NotSupportedException(); }
  229.         
  230.         public override  long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
  231.         public override  void SetLength(long value) { throw new NotSupportedException(); }
  232.         public override void Write(byte[] buffer, int offset, int count)
  233.         {
  234.             throw new NotSupportedException();
  235.         }        
  236.     } // HttpReadingStream
  237.     internal class HttpFixedLengthReadingStream : HttpReadingStream
  238.     {    
  239.         private HttpSocketHandler _inputStream = null;  // read content data from here     
  240.         private int _bytesLeft;                               // bytes left in current chunk
  241.        
  242.         internal HttpFixedLengthReadingStream(HttpSocketHandler inputStream, int contentLength)
  243.         {
  244.             _inputStream = inputStream;
  245.             _bytesLeft = contentLength;
  246.         } // HttpFixedLengthReadingStream
  247.         
  248.         public override bool FoundEnd { get { return _bytesLeft == 0; } }
  249.         public override void Close() {}
  250.         private int min(int a, int b) { return a < b ? a : b;}
  251.         
  252.         public override int Read(byte[] buffer, int offset, int count)
  253.         {
  254.             if (_bytesLeft == 0)
  255.                 return 0;
  256.         
  257.             int readCount = _inputStream.Read(buffer, offset, min(_bytesLeft, count));
  258.             if (readCount > 0)
  259.                 _bytesLeft -= readCount;
  260.             
  261.             return readCount;
  262.         } // Read
  263.         public override int ReadByte()
  264.         {
  265.             if (_bytesLeft == 0)
  266.                 return -1;
  267.             _bytesLeft -= 1;
  268.             return _inputStream.ReadByte();           
  269.         } // ReadByte
  270.                 
  271.     } // HttpFixedLengthReadingStream
  272.     // Stream class to read chunked data for HTTP
  273.     //   (assumes that provided outputStream will be positioned for
  274.     //    reading the body)    
  275.     internal class HttpChunkedReadingStream : HttpReadingStream
  276.     {
  277.         private static byte[] _trailer = Encoding.ASCII.GetBytes("0/r/n/r/n/r/n"); // 0-length, null trailer, end chunked
  278.         private static byte[] _endChunk = Encoding.ASCII.GetBytes("/r/n");
  279.    
  280.         private HttpSocketHandler _inputStream = null// read chunked http data from here
  281.         
  282.         private int    _bytesLeft;          // bytes left in current chunk
  283.         private bool   _bFoundEnd = false;  // has end of stream been reached?
  284.         private byte[] _byteBuffer = new byte[1]; // buffer for reading bytes
  285.         
  286.         
  287.         internal HttpChunkedReadingStream(HttpSocketHandler inputStream)
  288.         {
  289.             _inputStream = inputStream;
  290.             
  291.             _bytesLeft = 0;
  292.         } // HttpChunkedReadingStream
  293.         public override bool FoundEnd { get { return _bFoundEnd; } }
  294.         public override void Close() 
  295.         {
  296.         } // Close
  297.         
  298.         private int min(int a, int b) { return a < b ? a : b;}
  299.         
  300.         public override int Read(byte[] buffer, int offset, int count)
  301.         {
  302.             int bytesRead = 0;
  303.         
  304.             while (!_bFoundEnd && (count > 0))
  305.             {
  306.                 // see if we need to start reading a new chunk
  307.                 if (_bytesLeft == 0)
  308.                 {
  309.                     // this loop stops when the end of line is found
  310.                     for (;;)
  311.                     {
  312.                         byte b = (byte)_inputStream.ReadByte();
  313.                     
  314.                         // see if this is the end of the length
  315.                         if (b == '/r')
  316.                         {          
  317.                             // This had better be '/n'
  318.                             if ((char)_inputStream.ReadByte() != '/n')
  319.                             {
  320.                                 throw new RemotingException(
  321.                                     CoreChannel.GetResourceString(
  322.                                         "Remoting_Http_ChunkedEncodingError"));
  323.                             }
  324.                             else
  325.                                 break// we've finished reading the length
  326.                         }
  327.                         else
  328.                         {
  329.                             int value = HttpChannelHelper.CharacterHexDigitToDecimal(b);
  330.                             // make sure value is a hex-digit
  331.                             if ((value < 0) || (value > 15))
  332.                             {
  333.                                 throw new RemotingException(
  334.                                     CoreChannel.GetResourceString(
  335.                                         "Remoting_Http_ChunkedEncodingError"));
  336.                             }
  337.                             // update _bytesLeft value to account for new digit on the right
  338.                             _bytesLeft = (_bytesLeft * 16) + value;
  339.                         }
  340.                     }
  341.     
  342.                     if (_bytesLeft == 0)
  343.                     {
  344.                         // read off trailing headers and end-line
  345.                         String trailerHeader;
  346.                         do
  347.                         {
  348.                             trailerHeader = _inputStream.ReadToEndOfLine();
  349.                         } while (!(trailerHeader.Length == 0));
  350.                         
  351.                         _bFoundEnd = true;
  352.                     }
  353.                 }
  354.                 if (!_bFoundEnd)
  355.                 {
  356.                     int readCount = min(_bytesLeft, count);
  357.                     int bytesReadThisTime = _inputStream.Read(buffer, offset, readCount);
  358.                     if (bytesReadThisTime <= 0)
  359.                     {
  360.                         throw new RemotingException(
  361.                             CoreChannel.GetResourceString(
  362.                                 "Remoting_Http_ChunkedEncodingError"));
  363.                     }
  364.                     
  365.                     _bytesLeft -= bytesReadThisTime;
  366.                     count -= bytesReadThisTime;
  367.                     offset += bytesReadThisTime;
  368.                     bytesRead += bytesReadThisTime;
  369.                 
  370.                     // see if the end of the chunk was found
  371.                     if (_bytesLeft == 0)
  372.                     {
  373.                         // read off "/r/n"
  374.                         char ch = (char)_inputStream.ReadByte();
  375.                         if (ch != '/r')
  376.                         {
  377.                             throw new RemotingException(
  378.                                 CoreChannel.GetResourceString(
  379.                                     "Remoting_Http_ChunkedEncodingError"));
  380.                         }
  381.                         ch = (char)_inputStream.ReadByte();
  382.                         if (ch != '/n')
  383.                         {                      
  384.                             throw new RemotingException(
  385.                                 CoreChannel.GetResourceString(
  386.                                     "Remoting_Http_ChunkedEncodingError"));
  387.                         }
  388.                     }
  389.                 }
  390.             } // while (count > 0)  
  391.             return bytesRead;
  392.         } // Read        
  393.         public override int ReadByte()
  394.         {
  395.             int readCount = Read(_byteBuffer, 0, 1);
  396.             if (readCount == 0)
  397.                 return -1;
  398.                 
  399.             return _byteBuffer[0];         
  400.         } // ReadByte
  401.         
  402.     } // class HttpChunkedReadingStream
  403.     
  404.     [Serializable]
  405.     internal enum HttpVersion
  406.     {
  407.         V1_0,
  408.         V1_1
  409.     } // HttpVersion
  410.     // Maintains control of a socket connection.
  411.     internal class HttpServerSocketHandler : HttpSocketHandler
  412.     {
  413.         // Used to make sure verb characters are valid
  414.         private static ValidateByteDelegate s_validateVerbDelegate =
  415.             new ValidateByteDelegate(HttpServerSocketHandler.ValidateVerbCharacter);
  416.     
  417.         // Used to keep track of socket connections
  418.         private static Int64 _connectionIdCounter = 0;        
  419.         // primed buffer data
  420.         private static byte[] _bufferhttpContinue = Encoding.ASCII.GetBytes("HTTP/1.1 100 Continue/r/n/r/n");
  421.                 
  422.         // stream manager data
  423.         private HttpReadingStream _requestStream = null;  // request stream we handed out.
  424.         private HttpServerResponseStream _responseStream = null// response stream we handed out.
  425.         private Int64 _connectionId; // id for this connection
  426.         // request state flags
  427.         private HttpVersion _version; // http version used by client
  428.         
  429.         private int  _contentLength = 0;        // Content-Length value if found
  430.         private bool _chunkedEncoding = false;  // does request stream use chunked encoding?
  431.         private bool _keepAlive = false;        // does the client want to keep the connection alive?
  432.         private bool _allowChunkedResponse = false;  // can we send a chunked response?
  433.         
  434.         internal HttpServerSocketHandler(Socket socket, RequestQueue requestQueue) : base(socket, requestQueue)
  435.         {          
  436.             _connectionId = Interlocked.Increment(ref _connectionIdCounter);
  437.         } // HttpServerSocketHandler
  438.         public bool KeepAlive { get { return _keepAlive; } }
  439.         // FUTURE: Implement a response stream class that will only switch into chunked
  440.         //   mode once was the response grows beyond a certain size. Chunking is
  441.         //   disabled for now since without the custom stream, it degrades performance
  442.         //   for small calls where chunking is not needed.
  443.         public bool AllowChunkedResponse { get { return false && _allowChunkedResponse; } }
  444.         // Determine if it's possible to service another request
  445.         public bool CanServiceAnotherRequest()
  446.         {
  447.             if (_keepAlive && (_requestStream != null))           
  448.             {
  449.                 if (_requestStream.FoundEnd || _requestStream.ReadToEnd())
  450.                     return true;
  451.             }
  452.             return false;
  453.         } // CanServiceAnotherRequest
  454.         
  455.         // Prepare for reading a new request off of the same socket
  456.         protected override void PrepareForNewMessage()
  457.         {
  458.             _requestStream = null;
  459.             _responseStream = null;
  460.             
  461.             _contentLength = 0;
  462.             _chunkedEncoding = false;
  463.             _keepAlive = false
  464.             _allowChunkedResponse = false;
  465.         } // PrepareForNewRequest
  466.             
  467.         protected override void SendErrorMessageIfPossible(Exception e)
  468.         {
  469.             // If we haven't started sending a response back, we can do the following.
  470.             if ((_responseStream == null) && !(e is SocketException))
  471.             {
  472.                 SendResponse(null"500""Internal Server Error"null);
  473.             }                   
  474.         } // SendErrorMessageIfPossible
  475.         private static bool ValidateVerbCharacter(byte b)
  476.         {
  477.             if (Char.IsLetter((char)b) ||
  478.                 (b == '-'))
  479.             {
  480.                 return true;
  481.             }
  482.             return false;
  483.         } // ValidateVerbCharacter
  484.             
  485.         // read headers
  486.         public BaseTransportHeaders ReadHeaders()
  487.         {        
  488.             bool bSendContinue = false;
  489.         
  490.             BaseTransportHeaders headers = new BaseTransportHeaders();
  491.             // read first line
  492.             String verb, requestURI, version;
  493.             ReadFirstLine(out verb, out requestURI, out version);
  494.             if ((verb == null) || (requestURI == null) || (version == null))
  495.             {
  496.                 throw new RemotingException(
  497.                     CoreChannel.GetResourceString(
  498.                         "Remoting_Http_UnableToReadFirstLine"));
  499.             }
  500.             if (version.Equals("HTTP/1.1")) // most common case
  501.                 _version = HttpVersion.V1_1;
  502.             else
  503.             if (version.Equals("HTTP/1.0"))
  504.                 _version = HttpVersion.V1_0;
  505.             else
  506.                 _version = HttpVersion.V1_1; // (assume it will understand 1.1)
  507.             if (_version == HttpVersion.V1_1)
  508.             {
  509.                 _allowChunkedResponse = true;
  510.                 _keepAlive = true;
  511.             }   
  512.             else // it's a 1.0 client
  513.             {
  514.                 _allowChunkedResponse = false;
  515.                 _keepAlive = false;
  516.             }
  517.             
  518.             // update request uri to be sure that it has no channel data
  519.             String channelURI;
  520.             String objectURI;
  521.             channelURI = HttpChannelHelper.ParseURL(requestURI, out objectURI);
  522.             if (channelURI == null)
  523.             {
  524.                 objectURI = requestURI;
  525.             }                        
  526.             headers["__RequestVerb"] = verb;
  527.             headers.RequestUri = objectURI;
  528.             headers["__HttpVersion"] = version;
  529.             // check to see if we must send continue
  530.             if ((_version == HttpVersion.V1_1) &
  531.                 (verb.Equals("POST") || verb.Equals("PUT")))
  532.             {
  533.                 bSendContinue = true;
  534.             }            
  535.             ReadToEndOfHeaders(headers, out _chunkedEncoding, out _contentLength,
  536.                                ref _keepAlive, ref bSendContinue);       
  537.             if (bSendContinue && (_version != HttpVersion.V1_0))
  538.                 SendContinue();
  539.             // add IP address and Connection Id to headers
  540.             headers[CommonTransportKeys.IPAddress] = ((IPEndPoint)NetSocket.RemoteEndPoint).Address;
  541.             headers[CommonTransportKeys.ConnectionId] = _connectionId;
  542.             
  543.             return headers;
  544.         } // ReadHeaders
  545.         public Stream GetRequestStream()
  546.         {
  547.             if (_chunkedEncoding)
  548.                 _requestStream = new HttpChunkedReadingStream(this);
  549.             else
  550.                 _requestStream = new HttpFixedLengthReadingStream(this, _contentLength);
  551.             return _requestStream;
  552.         } // GetRequestStream
  553.         public Stream GetResponseStream(String statusCode, String reasonPhrase,
  554.                                         ITransportHeaders headers)
  555.         {
  556.             bool contentLengthPresent = false;
  557.             bool useChunkedEncoding = false;
  558.             int contentLength = 0;
  559.             // check for custom user status code and reason phrase
  560.             Object userStatusCode = headers["__HttpStatusCode"]; // someone might have stored an int
  561.             String userReasonPhrase = headers["__HttpReasonPhrase"as String;
  562.             if (userStatusCode != null)
  563.                 statusCode = userStatusCode.ToString();
  564.             if (userReasonPhrase != null)
  565.                 reasonPhrase = userReasonPhrase;
  566.             // see if we can handle any more requests on this socket
  567.             if (!CanServiceAnotherRequest())
  568.             {
  569.                 headers["Connection"] = "Close";
  570.             }                
  571.             // check for content length
  572.             Object contentLengthEntry = headers["Content-Length"];
  573.             if (contentLengthEntry != null)
  574.             {
  575.                 contentLengthPresent = true;
  576.                 if (contentLengthEntry is int)
  577.                     contentLength = (int)contentLengthEntry;
  578.                 else                
  579.                     contentLength = Convert.ToInt32(contentLengthEntry);
  580.             }
  581.             // see if we are going to use chunked-encoding
  582.             useChunkedEncoding = AllowChunkedResponse && !contentLengthPresent;
  583.             if (useChunkedEncoding)
  584.                 headers["Transfer-Encoding"] = "chunked";
  585.             // write headers to stream
  586.             ChunkedMemoryStream headerStream = new ChunkedMemoryStream(CoreChannel.BufferPool);
  587.             WriteResponseFirstLine(statusCode, reasonPhrase, headerStream);
  588.             WriteHeaders(headers, headerStream);
  589.             headerStream.WriteTo(NetStream);
  590.             headerStream.Close();
  591.           
  592.            
  593.             // return stream ready for content
  594.             if (useChunkedEncoding)
  595.                 _responseStream = new HttpChunkedResponseStream(NetStream);
  596.             else
  597.                 _responseStream = new HttpFixedLengthResponseStream(NetStream, contentLength);
  598.             return _responseStream;
  599.         } // GetResponseStream       
  600.         private bool ReadFirstLine(out String verb, out String requestURI, out String version)
  601.         {
  602.             verb = null;
  603.             requestURI = null;
  604.             version = null;
  605.             verb = ReadToChar(' ', s_validateVerbDelegate);
  606.             byte[] requestUriBytes = ReadToByte((byte)' ');
  607.             int decodedUriLength;
  608.             HttpChannelHelper.DecodeUriInPlace(requestUriBytes, out decodedUriLength);
  609.             requestURI = Encoding.UTF8.GetString(requestUriBytes, 0, decodedUriLength);
  610.             
  611.             version = ReadToEndOfLine();
  612.             return true;
  613.         } // ReadFirstLine
  614.         
  615.         private void SendContinue()
  616.         {
  617.             // Output:
  618.             // HTTP/1.1 100 Continue
  619.             
  620.             // Send the continue response back to the client
  621.             NetStream.Write(_bufferhttpContinue, 0, _bufferhttpContinue.Length);
  622.         } // SendContinue
  623.         public void SendResponse(Stream httpContentStream, 
  624.                                  String statusCode, String reasonPhrase,
  625.                                  ITransportHeaders headers)
  626.         {
  627.             if (_responseStream != null)
  628.             {
  629.                 _responseStream.Close();
  630.                 if (_responseStream != httpContentStream)
  631.                 {
  632.                     throw new RemotingException(
  633.                         "Http transport sink was not given the stream that it returned from GetResponseStream().");
  634.                 }
  635.                 // we are done with the response stream
  636.                 _responseStream = null;
  637.             }
  638.             else
  639.             {
  640.                 if (headers == null)
  641.                     headers = new TransportHeaders();
  642.                 String serverHeader = (String)headers["Server"];
  643.                 if (serverHeader != null)
  644.                     serverHeader = HttpServerTransportSink.ServerHeader + ", " + serverHeader;
  645.                 else
  646.                     serverHeader = HttpServerTransportSink.ServerHeader;
  647.                 headers["Server"] = serverHeader;
  648.             
  649.                 // Add length to response headers if necessary
  650.                 if (!AllowChunkedResponse && (httpContentStream != null))
  651.                     headers["Content-Length"] = httpContentStream.Length.ToString();
  652.                 else
  653.                 if (httpContentStream == null)
  654.                     headers["Content-Length"] = "0";
  655.             
  656.                 GetResponseStream(statusCode, reasonPhrase, headers);
  657.         
  658.                 // write HTTP content
  659.                 if(httpContentStream != null)
  660.                 {
  661.                     StreamHelper.CopyStream(httpContentStream, _responseStream);                    
  662.                     _responseStream.Close();
  663.                     httpContentStream.Close();
  664.                 }
  665.                 // we are done with the response stream
  666.                 _responseStream = null;
  667.             }
  668.         } // SendResponse
  669.        
  670.         
  671.     } // class HttpServerSocketHandler
  672. // namespace System.Runtime.Remoting.Channels.Http
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值