1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 using System.IO;
  8 using System.Collections;
  9 using System.Concepts;
 10 
 11 namespace System.Net.Http
 12 {
 13     public class ChunkExtensionAdder
 14     {
 15         public ChunkExtensionAdder(Map<stringstring>& chunkExtensions_) : chunkExtensions(chunkExtensions_)
 16         {
 17         }
 18         public void AddChunkExtension(const string& chunkExtNameconst string& chunkExtVal)
 19         {
 20             chunkExtensions[chunkExtName] = chunkExtVal;
 21         }
 22         private Map<stringstring>& chunkExtensions;
 23     }
 24     
 25     public void ReadCRLF(ByteStream& stream)
 26     {
 27         int x = stream.ReadByte();
 28         if (x != 13)
 29         {
 30             throw Exception("CR expected");
 31         }
 32         x = stream.ReadByte();
 33         if (x != 10)
 34         {
 35             throw Exception("LF expected");
 36         }
 37     }
 38     
 39     public ulong ReadChunkHeader(ByteStream& streamMap<stringstring>& chunkExtensions)
 40     {
 41         string s;
 42         int x = stream.ReadByte();
 43         int state = 0;
 44         while (x != -1)
 45         {
 46             byte b = cast<byte>(x);
 47             switch (state)
 48             {
 49                 case 0:
 50                 {
 51                     if (b == 13u)
 52                     {
 53                         state = 1;
 54                     }
 55                     else
 56                     {
 57                         s.Append(cast<char>(b));
 58                     }
 59                     break;
 60                 }
 61                 case 1:
 62                 {
 63                     if (b == 10u)
 64                     {
 65                         ulong chunkSize = 0u;
 66                         ChunkExtensionAdder adder(chunkExtensions);
 67                         HttpParser.ParseChunkHeader(s&chunkSize&adder);
 68                         return chunkSize;
 69                     }
 70                     else if (b != 13u)
 71                     {
 72                         state = 0;
 73                     }
 74                     break;
 75                 }
 76             }
 77             x = stream.ReadByte();
 78         }
 79         throw HttpException(HttpStatus("HTTP/1.1"statusClientErrorBadRequest"invalid HTTP message"));
 80     }
 81     
 82     public void ReadTrailer(ByteStream& streamList<HttpHeader>& trailer)
 83     {
 84         string line;
 85         int x = stream.ReadByte();
 86         int state = 0;
 87         while (x != -1)
 88         {
 89             byte b = cast<byte>(x);
 90             switch (state)
 91             {
 92                 case 0:
 93                 {
 94                     if (b == 13u)
 95                     {
 96                         state = 1;
 97                     }
 98                     else
 99                     {
100                         line.Append(cast<char>(b));
101                         state = 2;
102                     }
103                     break;
104                 }
105                 case 1:
106                 {
107                     if (b == 10u)
108                     {
109                         return;
110                     }
111                     else if (b != 13u)
112                     {
113                         line.Append(cast<char>(13));
114                         line.Append(cast<char>(b));
115                         state = 2;
116                     }
117                     break;
118                 }
119                 case 2:
120                 {
121                     if (b == 13u)
122                     {
123                         state = 3;
124                     }
125                     else
126                     {
127                         line.Append(cast<char>(b));
128                     }
129                     break;
130                 }
131                 case 3:
132                 {
133                     if (b == 10u)
134                     {
135                         if (!line.IsEmpty())
136                         {
137                             HttpHeader header = HttpParser.ParseHeader(line);
138                             trailer.Add(header);
139                             line.Clear();
140                         }
141                         state = 0;
142                     }
143                     else if (b != 13u)
144                     {
145                         line.Append(cast<char>(13));
146                         line.Append(cast<char>(b));
147                         state = 2;
148                     }
149                     else
150                     {
151                         line.Append(cast<char>(13));
152                     }
153                     break;
154                 }
155             }
156             x = stream.ReadByte();
157         }
158         throw HttpException(HttpStatus("HTTP/1.1"statusClientErrorBadRequest"invalid HTTP message"));
159     }
160     
161     public SharedPtr<ByteStream> ReadChunkedBody(ByteStream& streamList<HttpHeader>& trailer)
162     {
163         SharedPtr<ByteStream> resultStream(new BufferedByteStream(SharedPtr<ByteStream>(new MemoryByteStream())));
164         Map<stringstring> firstChunkExtensions;
165         ulong chunkSize = ReadChunkHeader(streamfirstChunkExtensions);
166         while (chunkSize != 0u)
167         {
168             for (ulong i = 0u; i < chunkSize; ++i;)
169             {
170                 int x = stream.ReadByte();
171                 if (x == -1)
172                 {
173                     throw Exception("unexpected end of stream");
174                 }
175                 byte b = cast<byte>(x);
176                 resultStream->Write(b);
177             }
178             ReadCRLF(stream);
179             Map<stringstring> nextChunkExtensions;
180             chunkSize = ReadChunkHeader(streamnextChunkExtensions);
181         }
182         ReadTrailer(streamtrailer);
183         return resultStream;
184     }
185 }