├── .gitignore ├── CosmosHttp.csproj ├── LICENSE.txt ├── README.md ├── resources └── icon.png └── source ├── Client ├── HttpRequest.cs └── HttpResponse.cs ├── Compress.cs └── HttpPacket.cs /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | .vs -------------------------------------------------------------------------------- /CosmosHttp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | True 6 | Cosmos 7 | Cosmos 8 | HTTP Client for CosmosOS. 9 | 1.0.4 10 | 11 | 12 | 13 | CosmosHttpClient 14 | icon.png 15 | LICENSE.txt 16 | https://github.com/CosmosOS/CosmosHttp 17 | https://github.com/CosmosOS/CosmosHttp 18 | README.md 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | True 34 | \ 35 | 36 | 37 | True 38 | \ 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, CosmosOS, COSMOS Project 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 

CosmosHTTP Client [WIP]

2 |

3 | 4 | Version 5 | 6 | 7 | License: BSD Clause 3 License 8 | 9 |

10 | 11 | > CosmosHTTP is a HTTP client made in C# for the Cosmos operating system construction kit. GET and PUT are currently supported. 12 | 13 | ### Todo 14 | See [this issue](https://github.com/CosmosOS/CosmosHttp/issues/1) for todo list. 15 | 16 | ## Usage 17 | 18 | ### Installation 19 | 20 | Install the Nuget Package from [Nuget](https://www.nuget.org/packages/CosmosHttp/): 21 | 22 | ```PM 23 | Install-Package CosmosHttp -Version 1.0.4 24 | ``` 25 | 26 | ```PM 27 | dotnet add PROJECT package CosmosHttp --version 1.0.4 28 | ``` 29 | 30 | Or add these lines to your Cosmos kernel .csproj: 31 | 32 | ``` 33 | 34 | 35 | 36 | ``` 37 | 38 | ### Examples 39 | 40 | ```CS 41 | using CosmosHttp.Client; 42 | 43 | HttpRequest request = new(); 44 | request.IP = "34.223.124.45"; 45 | request.Domain = "neverssl.com"; //very useful for subdomains on same IP 46 | request.Path = "/"; 47 | request.Method = "GET"; 48 | request.Send(); 49 | Console.WriteLine(request.Response.Content); // or to get bytes Encoding.ASCII.getString(request.Response.GetStream()) 50 | ``` 51 | 52 | Here is a basic wget command implementation using CosmosHttp: [github.com/aura-systems/Aura-Operating-System](https://github.com/aura-systems/Aura-Operating-System/blob/master/SRC/Aura_OS/System/Interpreter/Commands/Network/Wget.cs#L63). 53 | 54 | ## Authors 55 | 56 | 👤 **[@valentinbreiz](https://github.com/valentinbreiz)** 57 | 58 | 👤 **[@2881099](https://github.com/2881099)** 59 | 60 | ## 🤝 Contributing 61 | 62 | Contributions, issues and feature requests are welcome! Feel free to check [issues page](https://github.com/CosmosOS/CosmosHttp/issues). 63 | 64 | ## Show your support 65 | 66 | Give a ⭐️ if this project helped you! 67 | 68 | ## 📝 License 69 | 70 | Copyright © 2023 [CosmosOS](https://github.com/CosmosOS). This project is [BSD Clause 3](https://github.com/CosmosOS/CosmosHttp/blob/main/LICENSE.txt) licensed. 71 | -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CosmosOS/CosmosHttp/99b3ebd939f254abace205007efbcfd3da25a4d2/resources/icon.png -------------------------------------------------------------------------------- /source/Client/HttpRequest.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * PROJECT: CosmosHttp Development 3 | * CONTENT: Http Request class (Heavily inspered by https://github.com/2881099/TcpClientHttpRequest) 4 | * PROGRAMMERS: Valentin Charbonnier 5 | */ 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using System.Net; 11 | using System.Net.Sockets; 12 | using System.Text; 13 | 14 | namespace CosmosHttp.Client 15 | { 16 | public class HttpRequest : HttpPacket 17 | { 18 | private TcpClient _client; 19 | private string _remote; 20 | private string _path; 21 | private int _timeout = 20000; 22 | private NetworkStream _stream; 23 | private HttpResponse _response; 24 | 25 | public string Path 26 | { 27 | get 28 | { 29 | return _path; 30 | } 31 | set 32 | { 33 | _path = value; 34 | } 35 | } 36 | 37 | public HttpResponse Response 38 | { 39 | get { return _response; } 40 | } 41 | 42 | public HttpRequest() 43 | { 44 | _headers.Add("Connection", "Keep-Alive"); 45 | _headers.Add("Accept", "*/*"); 46 | _headers.Add("User-Agent", "CosmosHttp Client (CosmosOS)"); 47 | _headers.Add("Accept-Language", "en-us"); 48 | _headers.Add("Accept-Encoding", "gzip, deflate"); 49 | } 50 | 51 | public void Close() 52 | { 53 | if (_client != null) 54 | { 55 | if (_stream != null) 56 | { 57 | _stream.Close(); 58 | } 59 | _client.Close(); 60 | _client = null; 61 | } 62 | } 63 | 64 | public void Send() 65 | { 66 | Send(string.Empty); 67 | } 68 | 69 | public virtual void Send(string data) 70 | { 71 | Send(data, 0); 72 | } 73 | 74 | private void Send(string data, int redirections) 75 | { 76 | _data = data; 77 | 78 | _headers.Remove("Content-Length"); 79 | if (!string.IsNullOrEmpty(data) && string.Compare(_method, "post", true) == 0) 80 | { 81 | _headers["Content-Length"] = string.Concat(Encoding.ASCII.GetBytes(data).Length); 82 | if (string.IsNullOrEmpty(_headers["Content-Type"])) 83 | { 84 | _headers["Content-Type"] = "application/x-www-form-urlencoded; charset=" + _charset; 85 | } 86 | else if (_headers["Content-Type"].IndexOf("multipart/form-data") == -1) 87 | { 88 | if (_headers["Content-Type"].IndexOf("application/x-www-form-urlencoded") == -1) 89 | { 90 | _headers["Content-Type"] += "; application/x-www-form-urlencoded"; 91 | } 92 | if (_headers["Content-Type"].IndexOf("charset=") == -1) 93 | { 94 | _headers["Content-Type"] += "; charset=" + _charset; 95 | } 96 | } 97 | data += "\r\n\r\n"; 98 | } 99 | _headers["Host"] = _domain; 100 | 101 | string http = _method + " " + _path + " HTTP/1.1\r\n"; 102 | foreach (string head in _headers.Keys) 103 | { 104 | http += head + ": " + _headers[head] + "\r\n"; 105 | } 106 | 107 | http += "\r\n" + data; 108 | _head = http; 109 | byte[] request = Encoding.ASCII.GetBytes(http); 110 | if (_client == null || _remote == null) 111 | { 112 | _remote = _ip; 113 | this.Close(); 114 | _client = new TcpClient(_ip, 80); 115 | } 116 | try 117 | { 118 | _stream = getStream(); 119 | _stream.Write(request, 0, request.Length); 120 | } 121 | catch 122 | { 123 | this.Close(); 124 | _client = new TcpClient(_ip, 80); 125 | _stream = getStream(); 126 | _stream.Write(request, 0, request.Length); 127 | } 128 | receive(_stream, redirections, _ip); 129 | } 130 | 131 | protected void receive(Stream stream, int redirections, string action) 132 | { 133 | // stream.ReadTimeout = _timeout; TO PLUG 134 | _response = null; 135 | byte[] bytes = new byte[1024]; 136 | int bytesRead = 0; 137 | byte[] headBuffer = null; 138 | byte[] bodyBuffer = null; 139 | Exception exception = null; 140 | 141 | while (true) 142 | { 143 | int idx = -1; 144 | try 145 | { 146 | bytesRead = stream.Read(bytes, 0, bytes.Length); 147 | if (bytesRead == 0) 148 | { 149 | if (headBuffer == null || headBuffer.Length == 0) 150 | { 151 | throw new Exception("headBuffer is empty and no more data to read"); 152 | } 153 | break; 154 | } 155 | } 156 | catch (Exception e) 157 | { 158 | exception = e; 159 | break; 160 | } 161 | 162 | if (_response == null) 163 | { 164 | // Add the newly read bytes to the head buffer 165 | int oldLength = headBuffer != null ? headBuffer.Length : 0; 166 | byte[] newHeadBuffer = new byte[oldLength + bytesRead]; 167 | if (headBuffer != null) 168 | { 169 | Array.Copy(headBuffer, 0, newHeadBuffer, 0, oldLength); 170 | } 171 | Array.Copy(bytes, 0, newHeadBuffer, oldLength, bytesRead); 172 | headBuffer = newHeadBuffer; 173 | 174 | // Check for the header delimiter 175 | idx = Utils.findBytes(headBuffer, new byte[] { 13, 10, 13, 10 }, 0); 176 | if (idx != -1) 177 | { 178 | // Create the response with the header 179 | byte[] header = new byte[idx]; 180 | Array.Copy(headBuffer, 0, header, 0, idx); 181 | _response = new HttpResponse(this, header); 182 | _response.Received += headBuffer.Length - idx - 4; 183 | 184 | // Transfer remaining bytes to the body buffer 185 | int bodyLength = headBuffer.Length - idx - 4; 186 | bodyBuffer = new byte[bodyLength]; 187 | Array.Copy(headBuffer, idx + 4, bodyBuffer, 0, bodyLength); 188 | } 189 | } 190 | else 191 | { 192 | _response.Received += bytesRead; 193 | // Add the newly read bytes to the body buffer 194 | int oldLength = bodyBuffer != null ? bodyBuffer.Length : 0; 195 | byte[] newBodyBuffer = new byte[oldLength + bytesRead]; 196 | if (bodyBuffer != null) 197 | { 198 | Array.Copy(bodyBuffer, 0, newBodyBuffer, 0, oldLength); 199 | } 200 | Array.Copy(bytes, 0, newBodyBuffer, oldLength, bytesRead); 201 | bodyBuffer = newBodyBuffer; 202 | } 203 | 204 | if (_response != null) 205 | { 206 | if (_response.ContentLength >= 0) 207 | { 208 | if (_response.ContentLength <= bodyBuffer.Length) 209 | { 210 | break; 211 | } 212 | } 213 | } 214 | } 215 | 216 | if (_response == null) 217 | { 218 | this.closeTcp(); 219 | 220 | // Construct the request headers string 221 | List sb = new List(); 222 | sb.Add(_method.ToUpper() + " " + _ip + " HTTP/1.1"); 223 | foreach (string header in _headers.Keys) 224 | { 225 | sb.Add(header + ": " + _headers[header]); 226 | } 227 | 228 | // Throw a WebException with the appropriate message 229 | if (exception == null) 230 | { 231 | throw new WebException("WebException " + string.Join("\r\n", sb.ToArray())); 232 | } 233 | else 234 | { 235 | throw new WebException(exception.Message + "\r\n" + string.Join("\r\n", sb.ToArray()), exception); 236 | } 237 | } 238 | 239 | _response.SetStream(bodyBuffer); 240 | 241 | this.closeTcp(); 242 | } 243 | 244 | protected bool closeTcp() 245 | { 246 | this.Close(); 247 | return false; 248 | } 249 | 250 | protected NetworkStream getStream() 251 | { 252 | return _client.GetStream(); 253 | } 254 | } 255 | 256 | public class Utils 257 | { 258 | public static int findBytes(byte[] source, byte[] find, int startIndex) 259 | { 260 | if (find == null) return -1; 261 | if (find.Length == 0) return -1; 262 | if (source == null) return -1; 263 | if (source.Length == 0) return -1; 264 | if (startIndex < 0) startIndex = 0; 265 | int idx = -1, idx2 = startIndex - 1; 266 | do 267 | { 268 | idx2 = idx = Array.FindIndex(source, Math.Min(idx2 + 1, source.Length), delegate (byte b) { 269 | return b == find[0]; 270 | }); 271 | if (idx2 != -1) 272 | { 273 | for (int a = 1; a < find.Length; a++) 274 | { 275 | if (++idx2 >= source.Length || source[idx2] != find[a]) 276 | { 277 | idx = -1; 278 | break; 279 | } 280 | } 281 | if (idx != -1) break; 282 | } 283 | } while (idx2 != -1); 284 | return idx; 285 | } 286 | } 287 | } -------------------------------------------------------------------------------- /source/Client/HttpResponse.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * PROJECT: CosmosHttp Development 3 | * CONTENT: Http Response class (Heavily inspered by https://github.com/2881099/TcpClientHttpRequest) 4 | * PROGRAMMERS: Valentin Charbonnier 5 | */ 6 | 7 | using System; 8 | using System.Net; 9 | using System.Text; 10 | 11 | namespace CosmosHttp.Client 12 | { 13 | public class HttpResponse : HttpPacket 14 | { 15 | private int _received = 0; 16 | private HttpStatusCode _statusCode; 17 | private int _contentLength = -1; 18 | private string _contentType; 19 | private string _server; 20 | private string _content; 21 | private string _contentEncoding = string.Empty; 22 | private byte[] _stream = new byte[] { }; 23 | 24 | public int Received 25 | { 26 | get { return _received; } 27 | internal set { _received = value; } 28 | } 29 | 30 | public string TransferEncoding 31 | { 32 | get { return _headers["Transfer-Encoding"]; } 33 | set { _headers["Transfer-Encoding"] = value; } 34 | } 35 | 36 | public int ContentLength 37 | { 38 | get { return _contentLength; } 39 | } 40 | 41 | public string Content 42 | { 43 | get 44 | { 45 | if (_content == null) 46 | { 47 | _content = Encoding.ASCII.GetString(_stream); 48 | } 49 | return _content; 50 | } 51 | } 52 | 53 | public HttpResponse(HttpRequest ie, byte[] headBytes) 54 | { 55 | _ip = ie.IP; 56 | _method = ie.Method; 57 | _charset = ie.Charset; 58 | string head = Encoding.ASCII.GetString(headBytes); 59 | _head = head = head.Trim(); 60 | int idx = head.IndexOf(' '); 61 | if (idx != -1) 62 | { 63 | head = head.Substring(idx + 1); 64 | } 65 | idx = head.IndexOf(' '); 66 | if (idx != -1) 67 | { 68 | _statusCode = (HttpStatusCode)int.Parse(head.Remove(idx)); 69 | head = head.Substring(idx + 1); 70 | } 71 | idx = head.IndexOf("\r\n"); 72 | if (idx != -1) 73 | { 74 | head = head.Substring(idx + 2); 75 | } 76 | 77 | string[] heads = head.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); 78 | foreach (string h in heads) 79 | { 80 | string[] nv = h.Split(new char[] { ':' }, 2); 81 | if (nv.Length == 2) 82 | { 83 | string n = nv[0].Trim(); 84 | string v = nv[1].Trim(); 85 | 86 | // Handle specific headers and their unique cases 87 | switch (n.ToLower()) 88 | { 89 | case "content-length": 90 | if (!int.TryParse(v, out _contentLength)) _contentLength = -1; 91 | break; 92 | case "content-type": 93 | _contentType = v; 94 | idx = v.IndexOf("charset=", StringComparison.OrdinalIgnoreCase); 95 | if (idx != -1) 96 | { 97 | string charset = v.Substring(idx + 8).Split(';')[0].Trim(); 98 | if (string.Compare(_charset, charset, StringComparison.OrdinalIgnoreCase) != 0) 99 | { 100 | try 101 | { 102 | Encoding testEncode = Encoding.GetEncoding(charset); 103 | _charset = charset; 104 | } 105 | catch (Exception ex) 106 | { 107 | Cosmos.HAL.Global.debugger.Send("Ex: " + ex.ToString()); 108 | } 109 | } 110 | } 111 | break; 112 | case "server": 113 | _server = v; 114 | break; 115 | case "content-encoding": 116 | _contentEncoding = v; 117 | break; 118 | // Add more specific headers as needed 119 | default: 120 | if (_headers.ContainsKey(n)) 121 | { 122 | // Append or replace based on your requirement 123 | _headers[n] = v; 124 | } 125 | else 126 | { 127 | _headers.Add(n, v); 128 | } 129 | break; 130 | } 131 | } 132 | } 133 | } 134 | 135 | public void SetStream(byte[] bodyBytes) 136 | { 137 | _stream = bodyBytes; 138 | _contentLength = bodyBytes.Length; 139 | } 140 | 141 | public byte[] GetStream() 142 | { 143 | switch (_contentEncoding.ToLower()) 144 | { 145 | case "gzip": 146 | return GZip.Decompress(_stream); 147 | case "deflate": 148 | return Deflate.Decompress(_stream); 149 | default: 150 | return _stream; 151 | } 152 | } 153 | } 154 | } -------------------------------------------------------------------------------- /source/Compress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CosmosHttp 9 | { 10 | public static class GZip 11 | { 12 | public static byte[] Decompress(Stream stream) 13 | { 14 | try 15 | { 16 | stream.Position = 0; 17 | using (MemoryStream ms = new MemoryStream()) 18 | { 19 | using (Ionic.Zlib.GZipStream gzip = new Ionic.Zlib.GZipStream(stream, Ionic.Zlib.CompressionMode.Decompress)) 20 | { 21 | byte[] data = new byte[1024]; 22 | int size = 0; 23 | while ((size = gzip.Read(data, 0, data.Length)) > 0) 24 | { 25 | ms.Write(data, 0, size); 26 | } 27 | } 28 | return ms.ToArray(); 29 | } 30 | } 31 | catch { return (stream as MemoryStream).ToArray(); }; 32 | } 33 | public static byte[] Decompress(byte[] bt) 34 | { 35 | return Decompress(new MemoryStream(bt)); 36 | } 37 | public static byte[] Compress(string text) 38 | { 39 | return Compress(Encoding.UTF8.GetBytes(text)); 40 | } 41 | public static byte[] Compress(byte[] bt) 42 | { 43 | return Compress(bt, 0, bt.Length); 44 | } 45 | public static byte[] Compress(byte[] bt, int startIndex, int length) 46 | { 47 | using (MemoryStream ms = new MemoryStream()) 48 | { 49 | using (Ionic.Zlib.GZipStream gzip = new Ionic.Zlib.GZipStream(ms, Ionic.Zlib.CompressionMode.Compress)) 50 | { 51 | gzip.Write(bt, startIndex, length); 52 | } 53 | return ms.ToArray(); 54 | } 55 | } 56 | } 57 | 58 | public static class Deflate 59 | { 60 | public static byte[] Decompress(Stream stream) 61 | { 62 | try 63 | { 64 | stream.Position = 0; 65 | using (MemoryStream ms = new MemoryStream()) 66 | { 67 | using (Ionic.Zlib.DeflateStream def = new Ionic.Zlib.DeflateStream(stream, Ionic.Zlib.CompressionMode.Decompress)) 68 | { 69 | byte[] data = new byte[1024]; 70 | int size = 0; 71 | while ((size = def.Read(data, 0, data.Length)) > 0) 72 | { 73 | ms.Write(data, 0, size); 74 | } 75 | } 76 | return ms.ToArray(); 77 | } 78 | } 79 | catch { return (stream as MemoryStream).ToArray(); }; 80 | } 81 | public static byte[] Decompress(byte[] bt) 82 | { 83 | return Decompress(new MemoryStream(bt)); 84 | } 85 | public static byte[] Compress(string text) 86 | { 87 | return Compress(Encoding.UTF8.GetBytes(text)); 88 | } 89 | public static byte[] Compress(byte[] bt) 90 | { 91 | return Compress(bt, 0, bt.Length); 92 | } 93 | public static byte[] Compress(byte[] bt, int startIndex, int length) 94 | { 95 | using (MemoryStream ms = new MemoryStream()) 96 | { 97 | using (Ionic.Zlib.DeflateStream def = new Ionic.Zlib.DeflateStream(ms, Ionic.Zlib.CompressionMode.Compress)) 98 | { 99 | def.Write(bt, startIndex, length); 100 | } 101 | return ms.ToArray(); 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /source/HttpPacket.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * PROJECT: CosmosHttp Development 3 | * CONTENT: Base Http packet class (Heavily inspered by https://github.com/2881099/TcpClientHttpRequest) 4 | * PROGRAMMERS: Valentin Charbonnier 5 | */ 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | namespace CosmosHttp 11 | { 12 | public class HttpPacket : IDisposable 13 | { 14 | internal string _domain; 15 | internal string _ip; 16 | internal string _method = "GET"; 17 | internal string _charset = "us-ascii"; 18 | internal string _data; 19 | internal string _head; 20 | internal Dictionary _headers; 21 | 22 | public HttpPacket() 23 | { 24 | _headers = new Dictionary(); 25 | } 26 | 27 | public string Method 28 | { 29 | get 30 | { 31 | return _method; 32 | } 33 | set 34 | { 35 | _method = value.ToUpper(); 36 | } 37 | } 38 | 39 | public string Domain 40 | { 41 | get 42 | { 43 | return _domain; 44 | } 45 | set 46 | { 47 | _domain = value; 48 | } 49 | } 50 | 51 | public string IP 52 | { 53 | get 54 | { 55 | return _ip; 56 | } 57 | set 58 | { 59 | _ip = value; 60 | } 61 | } 62 | 63 | public string Charset 64 | { 65 | get 66 | { 67 | return _charset; 68 | } 69 | set 70 | { 71 | _charset = value; 72 | } 73 | } 74 | 75 | public Dictionary Headers 76 | { 77 | get { return _headers; } 78 | } 79 | 80 | public void Dispose() 81 | { 82 | } 83 | } 84 | } 85 | --------------------------------------------------------------------------------