Skip to content

Commit 304d209

Browse files
authored
release-v1.4.0
\r\n***PUBLISH_RELEASE***
2 parents aef88d1 + fa3bb28 commit 304d209

File tree

67 files changed

+5957
-445
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+5957
-445
lines changed

.github/workflows/update-dependencies.yml

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,32 +21,12 @@ jobs:
2121
name: Update .NET nanoFramework dependencies
2222
timeout-minutes: 15
2323
runs-on: windows-latest
24+
env:
25+
GITHUB_TOKEN: ${{ github.token }}
2426
steps:
2527
- name: Checkout
26-
uses: actions/checkout@v2.3.1
27-
with:
28-
path: main
29-
- name: Checkout tools repo
30-
uses: actions/checkout@v2.3.1
31-
with:
32-
repository: nanoframework/nf-tools
33-
path: tools
28+
uses: actions/checkout@v2
3429
- name: Update dependencies
35-
run: ./github-actions/update-nf-dependencies.ps1
36-
working-directory: tools
37-
- name: Create Pull Request
38-
uses: peter-evans/create-pull-request@v3
39-
if: env.CREATE_PR == 'true'
30+
uses: nanoframework/nanodu@v1
4031
with:
41-
title: '${{ env.PR_TITLE }}'
42-
body: |
43-
${{ env.PR_MESSAGE }}
44-
45-
[version update]
46-
47-
### :warning: This is an automated update. :warning:
48-
committer: 'nfbot <dependencybot@nanoframework.net>'
49-
branch: ${{ env.BRANCH_NAME }}
50-
path: main
51-
labels: |
52-
Type: dependencies
32+
solutionsToCheck: 'nanoFramework.System.Net.Http.sln'

README.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,118 @@
1717
| System.Net.Http.Server | [![Build Status](https://dev.azure.com/nanoframework/System.Net.Http/_apis/build/status/System.Net.Http?repoName=nanoframework%2FSystem.Net.Http&branchName=main)](https://dev.azure.com/nanoframework/System.Net.Http/_build/latest?definitionId=12&repoName=nanoframework%2FSystem.Net.Http&branchName=main) | [![NuGet](https://img.shields.io/nuget/v/nanoFramework.System.Net.Http.Server.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.System.Net.Http.Server/) |
1818
| System.Net.Http.Server (preview) | [![Build Status](https://dev.azure.com/nanoframework/System.Net.Http/_apis/build/status/System.Net.Http?repoName=nanoframework%2FSystem.Net.Http&branchName=develop)](https://dev.azure.com/nanoframework/System.Net.Http/_build/latest?definitionId=12&repoName=nanoframework%2FSystem.Net.Http&branchName=develop) | [![NuGet](https://img.shields.io/nuget/vpre/nanoFramework.System.Net.Http.Server.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.System.Net.Http.Server/) |
1919

20+
## Usage examples
21+
22+
The API, classes and namespaces in this library follow, as close as possible, the .NET ones.
23+
Exceptions are the lack of async calls, generics and the Task, async/await model. The names reflect that by dropping the _Async_ suffix and not returning `Task` and the lack of the overloaded methods with `CancelationToken` parameters.
24+
25+
Also worth mentioning is the need to pass the CA root certificate to `HttpClient` in order to be able to validate the server certificate.
26+
27+
## HttpClient calling REST services
28+
29+
`HttpClient` makes it very easy to connect and consume REST services.
30+
In order to use it, one has to create the object and them perform the calls. Note that `HttpClient` is meant to be reused throughout the application lifecycle. There is no need to create a new instance every time a call has to be performed. Like this:
31+
32+
```csharp
33+
static readonly HttpClient _httpClient = new HttpClient();
34+
```
35+
36+
To pass the CA root certificate allowing to validate the secure server certificate.
37+
The CA root cert can also come from a binary file or text file from a resource.
38+
39+
```csharp
40+
_httpClient.HttpsAuthentCert = new X509Certificate(
41+
@"-----BEGIN CERTIFICATE-----
42+
MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
43+
MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
44+
U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
45+
NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
46+
ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
47+
ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
48+
DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
49+
8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
50+
+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
51+
X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
52+
K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
53+
1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
54+
A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
55+
zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
56+
YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
57+
bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
58+
DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
59+
L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
60+
eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
61+
xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
62+
VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
63+
WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=
64+
-----END CERTIFICATE-----");
65+
```
66+
67+
It's possible to add HTTP headers that will be sent along with each request.
68+
69+
```csharp
70+
_httpClient.DefaultRequestHeaders.Add("x-ms-blob-type", "BlockBlob");
71+
```
72+
73+
### Perform a HTTP GET request
74+
75+
Here's a example of a HTTP request to read some content as a string:
76+
77+
```csharp
78+
HttpResponseMessage response = _httpClient.Get("https://httpbin.org/get");
79+
response.EnsureSuccessStatusCode();
80+
var responseBody = response.Content.ReadAsString();
81+
```
82+
83+
The above call would return something similar to the following, which can be output in Visual Studio by calling `Debug.WriteLine(responseBody)`:
84+
85+
```console
86+
{
87+
"args": {},
88+
"headers": {
89+
"Host": "httpbin.org",
90+
"X-Amzn-Trace-Id": "Root=1-6214aad3-38e5f8357bdf90530300eb5f",
91+
"X-Ms-Blob-Type": "BlockBlob"
92+
},
93+
"origin": "5.249.47.208",
94+
"url": "https://httpbin.org/get"
95+
}
96+
```
97+
98+
Note the call to `response.EnsureSuccessStatusCode();`. This will throw an `HttpRequestException` in case the status code from the HTTP request is not a successful one.
99+
100+
### Perform a HTTP POST request
101+
102+
Follows an example of a HTTP request performing POST request to send some json content to an endpoint.
103+
104+
```csharp
105+
var content = new StringContent("{\"someProperty\":\"someValue\"}", Encoding.UTF8, "application/json");
106+
var result = _httpClient.Post("https://httpbin.org/anything", content);
107+
result.EnsureSuccessStatusCode();
108+
```
109+
110+
Worth noting that the json content above it's presented as a simple string to simplify the code. There is a [json library](https://github.com/nanoframework/nanoFramework.Json) available to help with serializing and deserializing from/to C# classes, even the most complex ones.
111+
112+
Again, note the call to `response.EnsureSuccessStatusCode();` to make sure the HTTP request was successfully performed.
113+
114+
### Download binary content to a file
115+
116+
Using `HttpClient` makes it easy to deal with binary content too. Here's an example of how to download a file from a webserver.
117+
118+
```csharp
119+
HttpResponseMessage response = _httpClient.Get("https://storage-on-the-cloud.net/file-with-binary-content");
120+
response.EnsureSuccessStatusCode();
121+
122+
using FileStream fs = new FileStream($"I:\\i-am-a-binary-file.bin", FileMode.Create, FileAccess.Write);
123+
response.Content.ReadAsStream().CopyTo(fs);
124+
```
125+
126+
### Debugging through a reverse proxy
127+
128+
When code is deployed to a MCU it might be desirable to let the device connect to your development machine running IIS Express.
129+
This can be achieved with a proxy such as [this one](https://www.npmjs.com/package/iisexpress-proxy).
130+
Be aware that this leads to SocketExceptions with the current version of **nanoFramework** System.Net.Http when sending consecutive
131+
requests to your development machine. A simple retry mechanism in Debug mode will get around this.
20132

21133
## Feedback and documentation
22134

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
//
2+
// Copyright (c) .NET Foundation and Contributors
3+
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
4+
// See LICENSE file in the project root for full license information.
5+
//
6+
7+
using nanoFramework.TestFramework;
8+
using System;
9+
using System.IO;
10+
using System.Net.Http;
11+
using System.Text;
12+
13+
namespace HttpUnitTests
14+
{
15+
[TestClass]
16+
public class ByteArrayContentTest
17+
{
18+
[TestMethod]
19+
public void Ctor_NullSourceArray_ThrowsArgumentNullException()
20+
{
21+
Assert.Throws(typeof(ArgumentNullException), () => new ByteArrayContent(null));
22+
}
23+
24+
[TestMethod]
25+
public void Ctor_NullSourceArrayWithRange_ThrowsArgumentNullException()
26+
{
27+
Assert.Throws(typeof(ArgumentNullException), () => new ByteArrayContent(null, 0, 1));
28+
}
29+
30+
[TestMethod]
31+
public void Ctor_EmptySourceArrayWithRange_ThrowsArgumentOutOfRangeException()
32+
{
33+
Assert.Throws(typeof(ArgumentOutOfRangeException), () => new ByteArrayContent(new byte[0], 0, 1));
34+
}
35+
36+
[TestMethod]
37+
public void Ctor_StartIndexTooBig_ThrowsArgumentOufOfRangeException()
38+
{
39+
Assert.Throws(typeof(ArgumentOutOfRangeException), () => new ByteArrayContent(new byte[5], 5, 1));
40+
}
41+
42+
[TestMethod]
43+
public void Ctor_StartIndexNegative_ThrowsArgumentOutOfRangeException()
44+
{
45+
Assert.Throws(typeof(ArgumentOutOfRangeException), () => new ByteArrayContent(new byte[5], -1, 1));
46+
}
47+
48+
[TestMethod]
49+
public void Ctor_LengthTooBig_ThrowsArgumentOutOfRangeException()
50+
{
51+
Assert.Throws(typeof(ArgumentOutOfRangeException), () => new ByteArrayContent(new byte[5], 1, 5));
52+
}
53+
54+
[TestMethod]
55+
public void Ctor_LengthPlusOffsetCauseIntOverflow_ThrowsArgumentOutOfRangeException()
56+
{
57+
Assert.Throws(typeof(ArgumentOutOfRangeException), () => new ByteArrayContent(new byte[5], 1, int.MaxValue));
58+
}
59+
60+
[TestMethod]
61+
public void Ctor_LengthNegative_ThrowsArgumentOutOfRangeException()
62+
{
63+
Assert.Throws(typeof(ArgumentOutOfRangeException), () => new ByteArrayContent(new byte[5], 0, -1));
64+
}
65+
66+
// TODO need to fix processing of exception
67+
//[TestMethod]
68+
//public void ContentLength_UseWholeSourceArray_LengthMatchesArrayLength()
69+
//{
70+
// var contentData = new byte[10];
71+
// var content = new ByteArrayContent(contentData);
72+
73+
// Assert.Equal(contentData.Length, content.Headers.ContentLength);
74+
//}
75+
76+
// TODO need to fix processing of exception
77+
//[TestMethod]
78+
//public void ContentLength_UsePartialSourceArray_LengthMatchesArrayLength()
79+
//{
80+
// Assert.SkipTest("Test disabled on API failure");
81+
82+
// // TODO need to fix edge case in ByteArrayContent
83+
84+
// var contentData = new byte[10];
85+
// var content = new ByteArrayContent(contentData, 5, 3);
86+
87+
// Assert.Equal(3, content.Headers.ContentLength);
88+
//}
89+
90+
[TestMethod]
91+
public void ReadAsStreamAsync_EmptySourceArray_Succeed()
92+
{
93+
var content = new ByteArrayContent(new byte[0]);
94+
Stream stream = content.ReadAsStream();
95+
Assert.Equal(0, stream.Length);
96+
}
97+
98+
// TODO need to fix processing of exception
99+
//[TestMethod]
100+
//public void ReadAsStream_Call_MemoryStreamWrappingByteArrayReturned()
101+
//{
102+
// Assert.SkipTest("Test disabled on API failure");
103+
104+
// // TODO need to fix edge case in stream reader
105+
106+
// var contentData = new byte[10];
107+
// var content = new MockByteArrayContent(contentData, 5, 3);
108+
109+
// Stream stream = content.ReadAsStream();
110+
// Assert.False(stream.CanWrite);
111+
// Assert.Equal(3, stream.Length);
112+
// Assert.Equal(0, content.CopyToCount);
113+
//}
114+
115+
// TODO need to fix processing of exception
116+
//[TestMethod]
117+
//public void CopyTo_NullDestination_ThrowsArgumentNullException()
118+
//{
119+
// byte[] contentData = CreateSourceArray();
120+
// var content = new ByteArrayContent(contentData);
121+
122+
// Assert.Throws(typeof(ArgumentNullException),
123+
// () =>
124+
// {
125+
// content.CopyTo(null);
126+
// });
127+
//}
128+
129+
[TestMethod]
130+
public void CopyTo_UseWholeSourceArray_WholeContentCopied()
131+
{
132+
byte[] contentData = CreateSourceArray();
133+
var content = new ByteArrayContent(contentData);
134+
135+
var destination = new MemoryStream();
136+
content.CopyTo(destination);
137+
138+
Assert.Equal(contentData.Length, destination.Length);
139+
CheckResult(destination, 0);
140+
}
141+
142+
[TestMethod]
143+
public void CopyTo_UsePartialSourceArray_PartialContentCopied()
144+
{
145+
byte[] contentData = CreateSourceArray();
146+
var content = new ByteArrayContent(contentData, 3, 5);
147+
148+
var destination = new MemoryStream();
149+
content.CopyTo(destination);
150+
151+
Assert.Equal(5, destination.Length);
152+
CheckResult(destination, 3);
153+
}
154+
155+
// TODO need to fix processing of exception
156+
//[TestMethod]
157+
//public void CopyTo_UseEmptySourceArray_NothingCopied()
158+
//{
159+
// var contentData = new byte[0];
160+
// var content = new ByteArrayContent(contentData, 0, 0);
161+
162+
// var destination = new MemoryStream();
163+
// content.CopyTo(destination);
164+
165+
// Assert.Equal(0, destination.Length);
166+
//}
167+
168+
#region Helper methods
169+
170+
private static byte[] CreateSourceArray()
171+
{
172+
var contentData = new byte[10];
173+
for (int i = 0; i < contentData.Length; i++)
174+
{
175+
contentData[i] = (byte)(i % 256);
176+
}
177+
return contentData;
178+
}
179+
180+
private static void CheckResult(Stream destination, byte firstValue)
181+
{
182+
destination.Position = 0;
183+
var destinationData = new byte[destination.Length];
184+
int read = destination.Read(destinationData, 0, destinationData.Length);
185+
186+
Assert.Equal(destinationData.Length, read);
187+
Assert.Equal(firstValue, destinationData[0]);
188+
189+
for (int i = 1; i < read; i++)
190+
{
191+
Assert.True((destinationData[i] == (destinationData[i - 1] + 1)) ||
192+
((destinationData[i] == 0) && (destinationData[i - 1] != 0)));
193+
}
194+
}
195+
196+
private class MockByteArrayContent : ByteArrayContent
197+
{
198+
public int CopyToCount { get; private set; }
199+
200+
public MockByteArrayContent(byte[] content, int offset, int count)
201+
: base(content, offset, count)
202+
{
203+
}
204+
205+
protected override void SerializeToStream(Stream stream)
206+
{
207+
CopyToCount++;
208+
base.CopyTo(stream);
209+
}
210+
}
211+
212+
#endregion
213+
}
214+
}

0 commit comments

Comments
 (0)