The Labs \ Source Viewer \ SSCLI \ System.Net \ IReadChunkBytes

  1. //------------------------------------------------------------------------------
  2. // <copyright file="_ChunkParse.cs" company="Microsoft">
  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. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System.Net
  16. {
  17.    
  18. /*++
  19.         IReadChunkBytes - An interface for reading chunk streams.
  20.         An interface implemented by objects that read from chuked
  21.         streams. This interface is used by the chunk parsing
  22.         utility functions.
  23.     --*/   
  24.    
  25.     internal interface IReadChunkBytes
  26.     {
  27.        
  28.         int NextByte {
  29.             get;
  30.             set;
  31.         }
  32.        
  33.     }
  34.     // interface IReadChunkBytes
  35.     static internal class ChunkParse
  36.     {
  37.        
  38. /*++
  39.             SkipPastCRLF - Skip past the terminating CRLF.
  40.             An internal utility function. This is used to skip over any chunk
  41.             extensions or footers in a chunked response. We look through the
  42.             source bytes looking for the terminating CRLF, honoring any quoted
  43.             strings we see in the process.
  44.             Input:
  45.                     Source      - An IReadChunkBytes interface which we can
  46.                                     use to read the bytes from.
  47.             Returns:
  48.                 The number of bytes we consumed, or -1 if there wasn't enough
  49.                 in the buffer. We'll return a 0 if there's some sort of syntax
  50.                 error.
  51.         --*/       
  52.         static internal int SkipPastCRLF(IReadChunkBytes Source)
  53.         {
  54.             int BytesRead;
  55.             int Current;
  56.             bool Escape;
  57.             bool InQS;
  58.             bool HaveCR;
  59.             bool HaveLF;
  60.            
  61.             BytesRead = 0;
  62.             Escape = false;
  63.             InQS = false;
  64.             HaveCR = false;
  65.             HaveLF = false;
  66.            
  67.             GlobalLog.Enter("SkipPastCRLF");
  68.            
  69.             // Loop through the buffer, looking at each byte. We have a
  70.             // pseudo state machine going here, and the action we take
  71.             // with each byte depends on the state.
  72.            
  73.             Current = Source.NextByte;
  74.             BytesRead++;
  75.            
  76.             while (Current != -1) {
  77.                
  78.                 // If we saw a CR last time, it must be followed by an unescaped
  79.                 // LF, otherwise it's an error. If it is followed by a LF, and
  80.                 // we're not in a quoted string, then we've found our termination.
  81.                 // If we are in a quoted string, this could be the start of a LWS
  82.                 // production. Remember that, and next time through we'll
  83.                 // check to make sure that this is an LWS production.
  84.                
  85.                 if (HaveCR) {
  86.                     if (Current != '\n') {
  87.                         // CR w/o a trailing LF is an error.
  88.                         GlobalLog.Leave("SkipPastCRLF", 0);
  89.                         return 0;
  90.                     }
  91.                     else {
  92.                         if (Escape) {
  93.                             // We have an CR, but they're trying to escape the
  94.                             // LF. This is an error.
  95.                            
  96.                             GlobalLog.Leave("SkipPastCRLF", 0);
  97.                             return 0;
  98.                         }
  99.                        
  100.                         // If we're not in a quoted string, we're done.
  101.                        
  102.                         if (!InQS) {
  103.                             // We've found the terminating CRLF pair.
  104.                             GlobalLog.Leave("SkipPastCRLF", 0);
  105.                             return BytesRead;
  106.                         }
  107.                         else {
  108.                             // We're in a quoted string, so set HaveLF
  109.                             // so we remember to check for a LWS production next
  110.                             // time.
  111.                            
  112.                             HaveLF = true;
  113.                            
  114.                             // We've proceessed this byte, set Escape so we don't
  115.                             // do it again.
  116.                            
  117.                             Escape = true;
  118.                         }
  119.                     }
  120.                    
  121.                     // We're past the CR now, don't remember it any more.
  122.                    
  123.                     HaveCR = false;
  124.                 }
  125.                 else {
  126.                     // If HaveLF is set, we must be in a quoted string and have
  127.                     // seen a CRLF pair. Make sure this character is a space or
  128.                     // tab.
  129.                    
  130.                     if (HaveLF) {
  131.                         // We have a LF. For this to be valid, we have to have a
  132.                         // space or tab be the next character.
  133.                        
  134.                         if (Current != ' ' && Current != '\t') {
  135.                             // Not a continuation, so return an error.
  136.                             GlobalLog.Leave("SkipPastCRLF", 0);
  137.                             return 0;
  138.                         }
  139.                        
  140.                         Escape = true;
  141.                         // Don't process this byte again.
  142.                         HaveLF = false;
  143.                     }
  144.                 }
  145.                
  146.                 // If we're escaping, just skip the next character.
  147.                 if (!Escape) {
  148.                     switch (Current) {
  149.                         case '"':
  150.                             if (InQS) {
  151.                                 InQS = false;
  152.                             }
  153.                             else {
  154.                                 InQS = true;
  155.                             }
  156.                             break;
  157.                         case '\\':
  158.                             if (InQS) {
  159.                                 Escape = true;
  160.                             }
  161.                             break;
  162.                         case '\r':
  163.                             HaveCR = true;
  164.                             break;
  165.                         case '\n':
  166.                            
  167.                            
  168.                             // If we get here, we have a LF without a preceding CR.
  169.                             // This is an error.
  170.                            
  171.                             GlobalLog.Leave("SkipPastCRLF", 0);
  172.                             return 0;
  173.                         default:
  174.                            
  175.                             break;
  176.                     }
  177.                 }
  178.                 else {
  179.                     // We're escaping, do nothing but rest the Escape flag.
  180.                    
  181.                     Escape = false;
  182.                 }
  183.                
  184.                 Current = Source.NextByte;
  185.                 BytesRead++;
  186.             }
  187.            
  188.             // If we get here, we never saw the terminating CRLF, so return -1.
  189.            
  190.             GlobalLog.Leave("SkipPastCRLF", -1);
  191.             return -1;
  192.         }
  193.        
  194.        
  195. /*++
  196.             GetChunkSize - Read the chunk size.
  197.             An internal utility function. Reads the chunk size from a
  198.             byte soruce, and returns the size or -1 if there isn't enough data.
  199.             Input:
  200.                     Source      - An IReadChunkBytes interface which we can
  201.                                     use to read the bytes from.
  202.                     chunkSize  - Out parameter for chunk size.
  203.             Returns:
  204.                 The number of bytes we consumed, or -1 if there wasn't enough
  205.                 in the buffer. We'll also return 0 if we have some sort of
  206.                 syntax error.
  207.         --*/       
  208.         static internal int GetChunkSize(IReadChunkBytes Source, out int chunkSize)
  209.         {
  210.             int BytesRead;
  211.             int Size;
  212.             int Current;
  213.            
  214.             GlobalLog.Enter("GetChunkSize");
  215.             Size = 0;
  216.            
  217.             //
  218.             // Loop while we have data. If we run out of data and exit the loop
  219.             // at the bottom, then we've run out of data and haven't found the
  220.             // length.
  221.            
  222.             Current = Source.NextByte;
  223.             BytesRead = 0;
  224.            
  225.             if (Current == 10 || Current == 13) {
  226.                 GlobalLog.Print(" Got Char: " + Current.ToString("X"));
  227.                 BytesRead++;
  228.                 Current = Source.NextByte;
  229.             }
  230.            
  231.             while (Current != -1) {
  232.                 // Get the next byte, and decode it.
  233.                
  234.                 if (Current >= '0' && Current <= '9') {
  235.                     // Normalize it to 0 based.
  236.                    
  237.                     Current -= '0';
  238.                    
  239.                 }
  240.                 else {
  241.                     // If we're here, this might be a hex digit.
  242.                     // If it is, normalize it to 10 based.
  243.                    
  244.                     if (Current >= 'a' && Current <= 'f') {
  245.                         Current -= 'a';
  246.                     }
  247.                     else {
  248.                         if (Current >= 'A' && Current <= 'F') {
  249.                             Current -= 'A';
  250.                         }
  251.                         else {
  252.                             // Done with the decoding. If we haven't actually
  253.                             // decoded a digit yet, we'll end up returning
  254.                             // 0, which will signal the error.
  255.                            
  256.                            
  257.                             // Push back the byte we read and didn't use.
  258.                            
  259.                             Source.NextByte = Current;
  260.                            
  261.                             chunkSize = Size;
  262.                            
  263.                             // Return how many bytes we took.
  264.                            
  265.                             GlobalLog.Print("Chunk Size = " + Size.ToString());
  266.                             GlobalLog.Leave("GetChunkSize", BytesRead);
  267.                             return BytesRead;
  268.                            
  269.                         }
  270.                     }
  271.                    
  272.                     // If we get here we've had an A-F digit, add 10
  273.                     // to it to normalize it.
  274.                    
  275.                     Current += 10;
  276.                 }
  277.                
  278.                 // Update the size and our state.
  279.                
  280.                 Size *= 16;
  281.                 Size += Current;
  282.                
  283.                 BytesRead++;
  284.                 Current = Source.NextByte;
  285.             }
  286.            
  287.             chunkSize = Size;
  288.             GlobalLog.Print("*Chunk Size* = " + Size.ToString());
  289.             GlobalLog.Leave("GetChunkSize", -1);
  290.             return -1;
  291.         }
  292.     }
  293.     // ChunkParse
  294. }
  295. // namespace System.Net

Developer Fusion