├── .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 |
5 |
6 |
7 |
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 |
--------------------------------------------------------------------------------