More test/doc coverage, send sample
Signed-off-by: clemensv <clemensv@microsoft.com>
This commit is contained in:
parent
84d9f29b29
commit
a650ad5323
|
|
@ -14,6 +14,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpSend", "samples\HttpSend\HttpSend.csproj", "{F1B9B769-DB6B-481F-905C-24FE3B12E00E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -48,6 +50,18 @@ Global
|
|||
{95215090-BDE3-4628-9261-64B91FBE4665}.Release|x64.Build.0 = Release|Any CPU
|
||||
{95215090-BDE3-4628-9261-64B91FBE4665}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{95215090-BDE3-4628-9261-64B91FBE4665}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F1B9B769-DB6B-481F-905C-24FE3B12E00E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F1B9B769-DB6B-481F-905C-24FE3B12E00E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F1B9B769-DB6B-481F-905C-24FE3B12E00E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F1B9B769-DB6B-481F-905C-24FE3B12E00E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F1B9B769-DB6B-481F-905C-24FE3B12E00E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F1B9B769-DB6B-481F-905C-24FE3B12E00E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F1B9B769-DB6B-481F-905C-24FE3B12E00E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F1B9B769-DB6B-481F-905C-24FE3B12E00E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F1B9B769-DB6B-481F-905C-24FE3B12E00E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F1B9B769-DB6B-481F-905C-24FE3B12E00E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F1B9B769-DB6B-481F-905C-24FE3B12E00E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F1B9B769-DB6B-481F-905C-24FE3B12E00E}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
|||
134
README.md
134
README.md
|
|
@ -43,7 +43,13 @@ If a CloudEvents-prefixed transport header, like an HTTP header, is `string` typ
|
|||
|
||||
## Extensions
|
||||
|
||||
CloudEvent extensions are reflected by implementations of the `ICloudEventExtension` interface.
|
||||
CloudEvent extensions are represented by implementations of the `ICloudEventExtension`
|
||||
interface. The SDK includes strongly typed implementations for all offical CloudEvents
|
||||
extensions:
|
||||
|
||||
* `DistributedTracingExtension` for [distributed tracing](https://github.com/cloudevents/spec/blob/master/extensions/distributed-tracing.md)
|
||||
* `SampledRateExtension` for [sampled rate](https://github.com/cloudevents/spec/blob/master/extensions/sampled-rate.md)
|
||||
* `SequenceExtension` for [sequence](https://github.com/cloudevents/spec/blob/master/extensions/sequence.md)
|
||||
|
||||
Extension classes provides type-safe access to the extension attributes, and implement the
|
||||
required validations as well as type mappings. An extension object is always created as an
|
||||
|
|
@ -73,4 +79,130 @@ The extension can later be accessed via the `Extension<T>()` method:
|
|||
var s = cloudEvent.Extension<DistributedTracingExtension>().TraceParent
|
||||
```
|
||||
|
||||
All APIs where a `CloudEvent` is constructed from an incoming event (or request or
|
||||
response), allow for extension instances to be added to the respective methods, and
|
||||
the extensions are invoked in the mapping process, for instance to extract information
|
||||
from headers that deviate from the CloudEvents default mapping.
|
||||
|
||||
For instance, the server-side mapping for `HttpRequestMessage` allows adding
|
||||
extensions like this:
|
||||
|
||||
``` C#
|
||||
public async Task<HttpResponseMessage> Run( HttpRequestMessage req, ILogger log)
|
||||
{
|
||||
var cloudEvent = await req.ToCloudEventAsync(new DistributedTracingExtension());
|
||||
}
|
||||
```
|
||||
|
||||
## Transport Bindings
|
||||
|
||||
This SDK helps with mapping CloudEvents from and to messages or transport frames of
|
||||
popular .NET clients, but without getting in the way of your application's choices of
|
||||
whether you want to send an event via HTTP PUT or POST or how you want to handle
|
||||
settlement of transfers in AMQP or MQTT. The transport binding classes and extensions
|
||||
therefore don't wrap the send and receive operations; you still use the native
|
||||
API of the respective library.
|
||||
|
||||
### HTTP - System.Net.Http.HttpClient
|
||||
|
||||
The .NET [`HttpClient`](https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) uses
|
||||
the [`HttpContent`](https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpcontent)
|
||||
abstraction to wrap payloads for sending requests that carry entity bodies.
|
||||
|
||||
This SDK provides a [`CloudEventContent`] class derived from `HttpContent` that can be
|
||||
created from a `CloudEvent` instance, the desired `ContentMode` and an event formatter.
|
||||
|
||||
``` C#
|
||||
|
||||
var cloudEvent = new CloudEvent("com.example.myevent", new Uri("urn:example-com:mysource"))
|
||||
{
|
||||
ContentType = new ContentType(MediaTypeNames.Application.Json),
|
||||
Data = JsonConvert.SerializeObject("hey there!")
|
||||
};
|
||||
|
||||
var content = new CloudEventContent( cloudEvent,
|
||||
ContentMode.Structured,
|
||||
new JsonEventFormatter());
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
var result = (await httpClient.PostAsync(this.Url, content));
|
||||
```
|
||||
|
||||
For responses, `HttpClient` puts all custom headers onto the `HttpResponseMessage` rather
|
||||
than on the carried `HttpContent` instance. Therefore, if an event is retrieved with
|
||||
`HttpClient`, for instance from a queue-like structure, the `CloudEvent` is created from
|
||||
the response message object rather than the content object using the `ToCloudEvent()`
|
||||
extension method on `HttpResponseMessage`:
|
||||
|
||||
``` C#
|
||||
var httpClient = new HttpClient();
|
||||
// delete and receive message from top of the queue
|
||||
var result = await httpClient.DeleteAsync(new Uri("https://example.com/queue/messages/top"));
|
||||
if (HttpStatusCode.OK == result.StatusCode) {
|
||||
var receivedCloudEvent = await result.ToCloudEvent();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### HTTP - System.Net.HttpWebRequest
|
||||
|
||||
If your application uses the `HttpWebRequest` client, you can copy a CloudEvent into
|
||||
the request structure in structured or binary mode:
|
||||
|
||||
``` C#
|
||||
|
||||
HttpWebRequest httpWebRequest = WebRequest.CreateHttp("https://example.com/target");
|
||||
httpWebRequest.Method = "POST";
|
||||
await httpWebRequest.CopyFromAsync(cloudEvent, ContentMode.Structured, new JsonEventFormatter());
|
||||
```
|
||||
|
||||
Mind that the `Method` property must be set to an HTTP method that allows an entity body
|
||||
to be sent, otherwise the copy operation will fail.
|
||||
|
||||
### HTTP - System.Net.HttpListener (HttpRequestMessage)
|
||||
|
||||
On the server-side, you can extract a CloudEvent from the server-side `HttpRequestMessage`
|
||||
with the `ToCloudEventAsync()` extension. If your code handles `HttpRequestContext`,
|
||||
you will use the `Request` property:
|
||||
|
||||
```C#
|
||||
var cloudEvent = await context.Request.ToCloudEventAsync();
|
||||
```
|
||||
|
||||
If you use a functions framework that lets you handle `HttpResponseMessage` and return
|
||||
`HttpResponseMessage`, you will call the extension on the request object directly:
|
||||
|
||||
``` C#
|
||||
public async Task<HttpResponseMessage> Run( HttpRequestMessage req, ILogger log)
|
||||
{
|
||||
var cloudEvent = await req.ToCloudEventAsync();
|
||||
}
|
||||
```
|
||||
|
||||
The extension implementation will read the `ContentType` header of the incoming request and
|
||||
automatically select the correct built-in event format decoder. Your code can always pass an
|
||||
overriding format decoder instance as the first argument if needed.
|
||||
|
||||
If your HTTP handler needs to return a CloudEvent, you copy the `CloudEvent` into the
|
||||
response with the `CopyFromAsync()` extension method:
|
||||
|
||||
``` C#
|
||||
var cloudEvent = new CloudEvent("com.example.myevent", new Uri("urn:example-com:mysource"))
|
||||
{
|
||||
ContentType = new ContentType(MediaTypeNames.Application.Json),
|
||||
Data = JsonConvert.SerializeObject("hey there!")
|
||||
};
|
||||
|
||||
await context.Response.CopyFromAsync(cloudEvent,
|
||||
ContentMode.Structured,
|
||||
new JsonEventFormatter());
|
||||
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
||||
```
|
||||
|
||||
### AMQP
|
||||
|
||||
TBD
|
||||
|
||||
## MQTT
|
||||
|
||||
TBD
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.2.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\CloudNative.CloudEvents\CloudNative.CloudEvents.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) Cloud Native Foundation.
|
||||
// Licensed under the Apache 2.0 license.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace HttpSend
|
||||
{
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Mime;
|
||||
using System.Threading.Tasks;
|
||||
using CloudNative.CloudEvents;
|
||||
using McMaster.Extensions.CommandLineUtils;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
class Program
|
||||
{
|
||||
[Option(Description = "CloudEvents 'source' (default: urn:example-com:mysource:abc)", LongName = "source",
|
||||
ShortName = "s")]
|
||||
string Source { get; } = "urn:example-com:mysource:abc";
|
||||
|
||||
[Option(Description = "CloudEvents 'type' (default: com.example.myevent)", LongName = "type", ShortName = "t")]
|
||||
string Type { get; } = "com.example.myevent";
|
||||
|
||||
[Required,Option(Description = "HTTP(S) address to send the event to", LongName = "url", ShortName = "u"),]
|
||||
Uri Url { get; }
|
||||
|
||||
public static int Main(string[] args) => CommandLineApplication.Execute<Program>(args);
|
||||
|
||||
async Task OnExecuteAsync()
|
||||
{
|
||||
var cloudEvent = new CloudEvent(this.Type, new Uri(this.Source))
|
||||
{
|
||||
ContentType = new ContentType(MediaTypeNames.Application.Json),
|
||||
Data = JsonConvert.SerializeObject("hey there!")
|
||||
};
|
||||
|
||||
var content = new CloudEventContent(cloudEvent, ContentMode.Structured, new JsonEventFormatter());
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
var result = (await httpClient.PostAsync(this.Url, content));
|
||||
|
||||
Console.WriteLine(result.StatusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,29 +5,30 @@
|
|||
namespace CloudNative.CloudEvents
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
public static class HttpClientExtension
|
||||
{
|
||||
const string HttpHeaderPrefix = "ce-";
|
||||
|
||||
const string SpecVersionHttpHeader = HttpHeaderPrefix + "specversion";
|
||||
|
||||
static JsonEventFormatter jsonFormatter = new JsonEventFormatter();
|
||||
|
||||
public static Task CopyFromAsync(this HttpListenerResponse httpListenerResponse, CloudEvent cloudEvent, ContentMode contentMode, ICloudEventFormatter formatter)
|
||||
public static Task CopyFromAsync(this HttpListenerResponse httpListenerResponse, CloudEvent cloudEvent,
|
||||
ContentMode contentMode, ICloudEventFormatter formatter)
|
||||
{
|
||||
if (contentMode == ContentMode.Structured)
|
||||
{
|
||||
var buffer = formatter.EncodeStructuredEvent(cloudEvent, out var contentType, cloudEvent.Extensions.Values);
|
||||
var buffer =
|
||||
formatter.EncodeStructuredEvent(cloudEvent, out var contentType, cloudEvent.Extensions.Values);
|
||||
httpListenerResponse.ContentType = contentType.ToString();
|
||||
MapAttributesToListenerResponse(cloudEvent, httpListenerResponse);
|
||||
return httpListenerResponse.OutputStream.WriteAsync(buffer, 0, buffer.Length);
|
||||
|
|
@ -39,34 +40,13 @@ namespace CloudNative.CloudEvents
|
|||
return stream.CopyToAsync(httpListenerResponse.OutputStream);
|
||||
}
|
||||
|
||||
private static Stream MapDataAttributeToStream(CloudEvent cloudEvent, ICloudEventFormatter formatter)
|
||||
{
|
||||
Stream stream;
|
||||
if (cloudEvent.Data is byte[])
|
||||
{
|
||||
stream = new MemoryStream((byte[])cloudEvent.Data);
|
||||
}
|
||||
else if (cloudEvent.Data is string)
|
||||
{
|
||||
stream = new MemoryStream(Encoding.UTF8.GetBytes((string)cloudEvent.Data));
|
||||
}
|
||||
else if (cloudEvent.Data is Stream)
|
||||
{
|
||||
stream = (Stream)cloudEvent.Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream = new MemoryStream(formatter.EncodeAttribute(CloudEventAttributes.DataAttributeName, cloudEvent.Data, cloudEvent.Extensions.Values));
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
public static async Task CopyFromAsync(this HttpWebRequest httpWebRequest, CloudEvent cloudEvent, ContentMode contentMode, ICloudEventFormatter formatter)
|
||||
public static async Task CopyFromAsync(this HttpWebRequest httpWebRequest, CloudEvent cloudEvent,
|
||||
ContentMode contentMode, ICloudEventFormatter formatter)
|
||||
{
|
||||
if (contentMode == ContentMode.Structured)
|
||||
{
|
||||
var buffer = formatter.EncodeStructuredEvent(cloudEvent, out var contentType, cloudEvent.Extensions.Values);
|
||||
var buffer =
|
||||
formatter.EncodeStructuredEvent(cloudEvent, out var contentType, cloudEvent.Extensions.Values);
|
||||
httpWebRequest.ContentType = contentType.ToString();
|
||||
MapAttributesToWebRequest(cloudEvent, httpWebRequest);
|
||||
await (httpWebRequest.GetRequestStream()).WriteAsync(buffer, 0, buffer.Length);
|
||||
|
|
@ -79,68 +59,6 @@ namespace CloudNative.CloudEvents
|
|||
await stream.CopyToAsync(httpWebRequest.GetRequestStream());
|
||||
}
|
||||
|
||||
static void MapAttributesToListenerResponse(CloudEvent cloudEvent, HttpListenerResponse httpListenerResponse)
|
||||
{
|
||||
foreach (var attribute in cloudEvent.GetAttributes())
|
||||
{
|
||||
switch (attribute.Key)
|
||||
{
|
||||
case CloudEventAttributes.DataAttributeName:
|
||||
case CloudEventAttributes.ContentTypeAttributeName:
|
||||
break;
|
||||
default:
|
||||
if (attribute.Value is string)
|
||||
{
|
||||
httpListenerResponse.Headers.Add(HttpHeaderPrefix + attribute.Key, attribute.Value.ToString());
|
||||
}
|
||||
else if (attribute.Value is DateTime)
|
||||
{
|
||||
httpListenerResponse.Headers.Add(HttpHeaderPrefix + attribute.Key, ((DateTime)attribute.Value).ToString("o"));
|
||||
}
|
||||
else if (attribute.Value is Uri || attribute.Value is int)
|
||||
{
|
||||
httpListenerResponse.Headers.Add(HttpHeaderPrefix + attribute.Key, attribute.Value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
httpListenerResponse.Headers.Add(HttpHeaderPrefix + attribute.Key, Encoding.UTF8.GetString(jsonFormatter.EncodeAttribute(attribute.Key, attribute.Value, cloudEvent.Extensions.Values)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void MapAttributesToWebRequest(CloudEvent cloudEvent, HttpWebRequest httpWebRequest)
|
||||
{
|
||||
foreach (var attribute in cloudEvent.GetAttributes())
|
||||
{
|
||||
switch (attribute.Key)
|
||||
{
|
||||
case CloudEventAttributes.DataAttributeName:
|
||||
case CloudEventAttributes.ContentTypeAttributeName:
|
||||
break;
|
||||
default:
|
||||
if (attribute.Value is string)
|
||||
{
|
||||
httpWebRequest.Headers.Add(HttpHeaderPrefix + attribute.Key, attribute.Value.ToString());
|
||||
}
|
||||
else if (attribute.Value is DateTime)
|
||||
{
|
||||
httpWebRequest.Headers.Add(HttpHeaderPrefix + attribute.Key, ((DateTime)attribute.Value).ToString("o"));
|
||||
}
|
||||
else if (attribute.Value is Uri || attribute.Value is int)
|
||||
{
|
||||
httpWebRequest.Headers.Add(HttpHeaderPrefix + attribute.Key, attribute.Value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
httpWebRequest.Headers.Add(HttpHeaderPrefix + attribute.Key, Encoding.UTF8.GetString(jsonFormatter.EncodeAttribute(attribute.Key, attribute.Value, cloudEvent.Extensions.Values)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool HasCloudEvent(this HttpResponseMessage httpResponseMessage)
|
||||
{
|
||||
return ((httpResponseMessage.Content.Headers.ContentType != null &&
|
||||
|
|
@ -160,19 +78,26 @@ namespace CloudNative.CloudEvents
|
|||
return ToCloudEventInternalAsync(httpResponseMessage, formatter, extensions);
|
||||
}
|
||||
|
||||
public static Task<CloudEvent> ToCloudEventAsync(this HttpListenerRequest httpListenerRequest,
|
||||
params ICloudEventExtension[] extensions)
|
||||
{
|
||||
return ToCloudEventAsync(httpListenerRequest, null, extensions);
|
||||
}
|
||||
|
||||
public static async Task<CloudEvent> ToCloudEventAsync(this HttpListenerRequest httpListenerRequest,
|
||||
ICloudEventFormatter formatter,
|
||||
ICloudEventFormatter formatter = null,
|
||||
params ICloudEventExtension[] extensions)
|
||||
{
|
||||
if (httpListenerRequest.ContentType != null &&
|
||||
httpListenerRequest.ContentType.StartsWith(CloudEvent.MediaType,
|
||||
StringComparison.InvariantCultureIgnoreCase))
|
||||
httpListenerRequest.ContentType.StartsWith(CloudEvent.MediaType,
|
||||
StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
// handle structured mode
|
||||
if (formatter == null)
|
||||
{
|
||||
// if we didn't get a formatter, pick one
|
||||
if (httpListenerRequest.ContentType.EndsWith(JsonEventFormatter.MediaTypeSuffix, StringComparison.InvariantCultureIgnoreCase))
|
||||
if (httpListenerRequest.ContentType.EndsWith(JsonEventFormatter.MediaTypeSuffix,
|
||||
StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
formatter = jsonFormatter;
|
||||
}
|
||||
|
|
@ -193,7 +118,8 @@ namespace CloudNative.CloudEvents
|
|||
if (httpResponseHeader.StartsWith(HttpHeaderPrefix, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
string headerValue = httpListenerRequest.Headers[httpResponseHeader];
|
||||
if (headerValue.StartsWith("{") && headerValue.EndsWith("}") || headerValue.StartsWith("[") && headerValue.EndsWith("]"))
|
||||
if (headerValue.StartsWith("{") && headerValue.EndsWith("}") ||
|
||||
headerValue.StartsWith("[") && headerValue.EndsWith("]"))
|
||||
{
|
||||
attributes[httpResponseHeader.Substring(3).ToLowerInvariant()] =
|
||||
JsonConvert.DeserializeObject(headerValue);
|
||||
|
|
@ -213,6 +139,101 @@ namespace CloudNative.CloudEvents
|
|||
}
|
||||
}
|
||||
|
||||
static void MapAttributesToListenerResponse(CloudEvent cloudEvent, HttpListenerResponse httpListenerResponse)
|
||||
{
|
||||
foreach (var attribute in cloudEvent.GetAttributes())
|
||||
{
|
||||
switch (attribute.Key)
|
||||
{
|
||||
case CloudEventAttributes.DataAttributeName:
|
||||
case CloudEventAttributes.ContentTypeAttributeName:
|
||||
break;
|
||||
default:
|
||||
if (attribute.Value is string)
|
||||
{
|
||||
httpListenerResponse.Headers.Add(HttpHeaderPrefix + attribute.Key,
|
||||
attribute.Value.ToString());
|
||||
}
|
||||
else if (attribute.Value is DateTime)
|
||||
{
|
||||
httpListenerResponse.Headers.Add(HttpHeaderPrefix + attribute.Key,
|
||||
((DateTime)attribute.Value).ToString("o"));
|
||||
}
|
||||
else if (attribute.Value is Uri || attribute.Value is int)
|
||||
{
|
||||
httpListenerResponse.Headers.Add(HttpHeaderPrefix + attribute.Key,
|
||||
attribute.Value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
httpListenerResponse.Headers.Add(HttpHeaderPrefix + attribute.Key,
|
||||
Encoding.UTF8.GetString(jsonFormatter.EncodeAttribute(attribute.Key, attribute.Value,
|
||||
cloudEvent.Extensions.Values)));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void MapAttributesToWebRequest(CloudEvent cloudEvent, HttpWebRequest httpWebRequest)
|
||||
{
|
||||
foreach (var attribute in cloudEvent.GetAttributes())
|
||||
{
|
||||
switch (attribute.Key)
|
||||
{
|
||||
case CloudEventAttributes.DataAttributeName:
|
||||
case CloudEventAttributes.ContentTypeAttributeName:
|
||||
break;
|
||||
default:
|
||||
if (attribute.Value is string)
|
||||
{
|
||||
httpWebRequest.Headers.Add(HttpHeaderPrefix + attribute.Key, attribute.Value.ToString());
|
||||
}
|
||||
else if (attribute.Value is DateTime)
|
||||
{
|
||||
httpWebRequest.Headers.Add(HttpHeaderPrefix + attribute.Key,
|
||||
((DateTime)attribute.Value).ToString("o"));
|
||||
}
|
||||
else if (attribute.Value is Uri || attribute.Value is int)
|
||||
{
|
||||
httpWebRequest.Headers.Add(HttpHeaderPrefix + attribute.Key, attribute.Value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
httpWebRequest.Headers.Add(HttpHeaderPrefix + attribute.Key,
|
||||
Encoding.UTF8.GetString(jsonFormatter.EncodeAttribute(attribute.Key, attribute.Value,
|
||||
cloudEvent.Extensions.Values)));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Stream MapDataAttributeToStream(CloudEvent cloudEvent, ICloudEventFormatter formatter)
|
||||
{
|
||||
Stream stream;
|
||||
if (cloudEvent.Data is byte[])
|
||||
{
|
||||
stream = new MemoryStream((byte[])cloudEvent.Data);
|
||||
}
|
||||
else if (cloudEvent.Data is string)
|
||||
{
|
||||
stream = new MemoryStream(Encoding.UTF8.GetBytes((string)cloudEvent.Data));
|
||||
}
|
||||
else if (cloudEvent.Data is Stream)
|
||||
{
|
||||
stream = (Stream)cloudEvent.Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream = new MemoryStream(formatter.EncodeAttribute(CloudEventAttributes.DataAttributeName,
|
||||
cloudEvent.Data, cloudEvent.Extensions.Values));
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
static async Task<CloudEvent> ToCloudEventInternalAsync(HttpResponseMessage httpResponseMessage,
|
||||
ICloudEventFormatter formatter, ICloudEventExtension[] extensions)
|
||||
|
|
@ -245,10 +266,12 @@ namespace CloudNative.CloudEvents
|
|||
var attributes = cloudEvent.GetAttributes();
|
||||
foreach (var httpResponseHeader in httpResponseMessage.Headers)
|
||||
{
|
||||
if (httpResponseHeader.Key.StartsWith(HttpHeaderPrefix, StringComparison.InvariantCultureIgnoreCase))
|
||||
if (httpResponseHeader.Key.StartsWith(HttpHeaderPrefix,
|
||||
StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
string headerValue = httpResponseHeader.Value.First();
|
||||
if (headerValue.StartsWith("{") && headerValue.EndsWith("}") || headerValue.StartsWith("[") && headerValue.EndsWith("]"))
|
||||
if (headerValue.StartsWith("{") && headerValue.EndsWith("}") ||
|
||||
headerValue.StartsWith("[") && headerValue.EndsWith("]"))
|
||||
{
|
||||
attributes[httpResponseHeader.Key.Substring(3).ToLowerInvariant()] =
|
||||
JsonConvert.DeserializeObject(headerValue);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
attrs["comexampleextension1"] = "value";
|
||||
attrs["comexampleextension2"] = new { othervalue = 5 };
|
||||
|
||||
Assert.Equal("0.1", cloudEvent.SpecVersion);
|
||||
Assert.Equal("0.2", cloudEvent.SpecVersion);
|
||||
Assert.Equal("com.github.pull.create", cloudEvent.Type);
|
||||
Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), cloudEvent.Source);
|
||||
Assert.Equal("A234-1234-1234", cloudEvent.Id);
|
||||
|
|
@ -57,7 +57,7 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
attrs["comexampleextension1"] = "value";
|
||||
attrs["comexampleextension2"] = new { othervalue = 5 };
|
||||
|
||||
Assert.Equal("0.1", cloudEvent.SpecVersion);
|
||||
Assert.Equal("0.2", cloudEvent.SpecVersion);
|
||||
Assert.Equal("com.github.pull.create", cloudEvent.Type);
|
||||
Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), cloudEvent.Source);
|
||||
Assert.Equal("A234-1234-1234", cloudEvent.Id);
|
||||
|
|
@ -95,7 +95,7 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
Data = "<much wow=\"xml\"/>"
|
||||
};
|
||||
|
||||
Assert.Equal("0.1", cloudEvent.SpecVersion);
|
||||
Assert.Equal("0.2", cloudEvent.SpecVersion);
|
||||
Assert.Equal("com.github.pull.create", cloudEvent.Type);
|
||||
Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), cloudEvent.Source);
|
||||
Assert.Equal("A234-1234-1234", cloudEvent.Id);
|
||||
|
|
|
|||
|
|
@ -6,21 +6,23 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
{
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Mime;
|
||||
using System.Security.Authentication.ExtendedProtection;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
public class HttpTest : IDisposable
|
||||
{
|
||||
private const string listenerAddress = "http://localhost:52671/";
|
||||
private const string testContextHeader = "testcontext";
|
||||
const string listenerAddress = "http://localhost:52671/";
|
||||
|
||||
const string testContextHeader = "testcontext";
|
||||
|
||||
HttpListener listener;
|
||||
ConcurrentDictionary<string, Func<HttpListenerContext, Task>> pendingRequests = new ConcurrentDictionary<string, Func<HttpListenerContext, Task>>();
|
||||
|
||||
ConcurrentDictionary<string, Func<HttpListenerContext, Task>> pendingRequests =
|
||||
new ConcurrentDictionary<string, Func<HttpListenerContext, Task>>();
|
||||
|
||||
public HttpTest()
|
||||
{
|
||||
|
|
@ -37,7 +39,11 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
HandleContext(t.Result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
listener.Stop();
|
||||
}
|
||||
|
||||
async Task HandleContext(HttpListenerContext requestContext)
|
||||
|
|
@ -58,52 +64,29 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
#pragma warning restore 4014
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
listener.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
async Task HttpStructuredClientSendTest()
|
||||
async Task HttpBinaryClientReceiveTest()
|
||||
{
|
||||
var cloudEvent = new CloudEvent("com.github.pull.create",
|
||||
new Uri("https://github.com/cloudevents/spec/pull/123"))
|
||||
{
|
||||
Id = "A234-1234-1234",
|
||||
Time = new DateTime(2018, 4, 5, 17, 31, 0, DateTimeKind.Utc),
|
||||
ContentType = new ContentType(MediaTypeNames.Text.Xml),
|
||||
Data = "<much wow=\"xml\"/>"
|
||||
};
|
||||
|
||||
var attrs = cloudEvent.GetAttributes();
|
||||
attrs["comexampleextension1"] = "value";
|
||||
attrs["comexampleextension2"] = new { othervalue = 5 };
|
||||
|
||||
|
||||
string ctx = Guid.NewGuid().ToString();
|
||||
var content = new CloudEventContent(cloudEvent, ContentMode.Structured, new JsonEventFormatter());
|
||||
content.Headers.Add(testContextHeader, ctx);
|
||||
|
||||
|
||||
pendingRequests.TryAdd(ctx, async context =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var receivedCloudEvent = await context.Request.ToCloudEventAsync(new JsonEventFormatter());
|
||||
var cloudEvent = new CloudEvent("com.github.pull.create",
|
||||
new Uri("https://github.com/cloudevents/spec/pull/123"))
|
||||
{
|
||||
Id = "A234-1234-1234",
|
||||
Time = new DateTime(2018, 4, 5, 17, 31, 0, DateTimeKind.Utc),
|
||||
ContentType = new ContentType(MediaTypeNames.Text.Xml),
|
||||
Data = "<much wow=\"xml\"/>"
|
||||
};
|
||||
|
||||
Assert.Equal("0.1", receivedCloudEvent.SpecVersion);
|
||||
Assert.Equal("com.github.pull.create", receivedCloudEvent.Type);
|
||||
Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), receivedCloudEvent.Source);
|
||||
Assert.Equal("A234-1234-1234", receivedCloudEvent.Id);
|
||||
Assert.Equal(DateTime.Parse("2018-04-05T17:31:00Z").ToUniversalTime(),
|
||||
receivedCloudEvent.Time.Value.ToUniversalTime());
|
||||
Assert.Equal(new ContentType(MediaTypeNames.Text.Xml), receivedCloudEvent.ContentType);
|
||||
Assert.Equal("<much wow=\"xml\"/>", receivedCloudEvent.Data);
|
||||
var attrs = cloudEvent.GetAttributes();
|
||||
attrs["comexampleextension1"] = "value";
|
||||
attrs["comexampleextension2"] = new { othervalue = 5 };
|
||||
|
||||
var attr = receivedCloudEvent.GetAttributes();
|
||||
Assert.Equal("value", (string)attr["comexampleextension1"]);
|
||||
Assert.Equal(5, (int)((dynamic)attr["comexampleextension2"]).othervalue);
|
||||
context.Response.StatusCode = (int)HttpStatusCode.NoContent;
|
||||
await context.Response.CopyFromAsync(cloudEvent, ContentMode.Binary, new JsonEventFormatter());
|
||||
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -112,23 +95,40 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
sw.Write(e.ToString());
|
||||
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.Close();
|
||||
});
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
var result = (await httpClient.PostAsync(new Uri(listenerAddress + "ep"), content));
|
||||
if (result.StatusCode != HttpStatusCode.NoContent)
|
||||
httpClient.DefaultRequestHeaders.Add(testContextHeader, ctx);
|
||||
var result = await httpClient.GetAsync(new Uri(listenerAddress + "ep"));
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||
var receivedCloudEvent = await result.ToCloudEvent();
|
||||
|
||||
Assert.Equal("0.2", receivedCloudEvent.SpecVersion);
|
||||
Assert.Equal("com.github.pull.create", receivedCloudEvent.Type);
|
||||
Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), receivedCloudEvent.Source);
|
||||
Assert.Equal("A234-1234-1234", receivedCloudEvent.Id);
|
||||
Assert.Equal(DateTime.Parse("2018-04-05T17:31:00Z").ToUniversalTime(),
|
||||
receivedCloudEvent.Time.Value.ToUniversalTime());
|
||||
Assert.Equal(new ContentType(MediaTypeNames.Text.Xml), receivedCloudEvent.ContentType);
|
||||
using (var sr = new StreamReader((Stream)receivedCloudEvent.Data))
|
||||
{
|
||||
throw new InvalidOperationException(result.Content.ReadAsStringAsync().GetAwaiter().GetResult());
|
||||
Assert.Equal("<much wow=\"xml\"/>", sr.ReadToEnd());
|
||||
}
|
||||
|
||||
var attr = receivedCloudEvent.GetAttributes();
|
||||
Assert.Equal("value", (string)attr["comexampleextension1"]);
|
||||
Assert.Equal(5, (int)((dynamic)attr["comexampleextension2"]).othervalue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
async Task HttpBinaryClientSendTest()
|
||||
{
|
||||
var cloudEvent = new CloudEvent("com.github.pull.create",
|
||||
new Uri("https://github.com/cloudevents/spec/pull/123"))
|
||||
new Uri("https://github.com/cloudevents/spec/pull/123"))
|
||||
{
|
||||
Id = "A234-1234-1234",
|
||||
Time = new DateTime(2018, 4, 5, 17, 31, 0, DateTimeKind.Utc),
|
||||
|
|
@ -140,19 +140,17 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
attrs["comexampleextension1"] = "value";
|
||||
attrs["comexampleextension2"] = new { othervalue = 5 };
|
||||
|
||||
|
||||
string ctx = Guid.NewGuid().ToString();
|
||||
var content = new CloudEventContent(cloudEvent, ContentMode.Binary, new JsonEventFormatter());
|
||||
content.Headers.Add(testContextHeader, ctx);
|
||||
|
||||
|
||||
pendingRequests.TryAdd(ctx, async context =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var receivedCloudEvent = await context.Request.ToCloudEventAsync(new JsonEventFormatter());
|
||||
|
||||
Assert.Equal("0.1", receivedCloudEvent.SpecVersion);
|
||||
Assert.Equal("0.2", receivedCloudEvent.SpecVersion);
|
||||
Assert.Equal("com.github.pull.create", receivedCloudEvent.Type);
|
||||
Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), receivedCloudEvent.Source);
|
||||
Assert.Equal("A234-1234-1234", receivedCloudEvent.Id);
|
||||
|
|
@ -178,6 +176,7 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.Close();
|
||||
});
|
||||
|
||||
|
|
@ -189,14 +188,12 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
async Task HttpStructuredClientReceiveTest()
|
||||
{
|
||||
string ctx = Guid.NewGuid().ToString();
|
||||
pendingRequests.TryAdd(ctx, async context =>
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
var cloudEvent = new CloudEvent("com.github.pull.create",
|
||||
|
|
@ -212,7 +209,6 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
attrs["comexampleextension1"] = "value";
|
||||
attrs["comexampleextension2"] = new { othervalue = 5 };
|
||||
|
||||
|
||||
await context.Response.CopyFromAsync(cloudEvent, ContentMode.Structured, new JsonEventFormatter());
|
||||
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
||||
}
|
||||
|
|
@ -224,66 +220,7 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
}
|
||||
}
|
||||
context.Response.Close();
|
||||
});
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
httpClient.DefaultRequestHeaders.Add(testContextHeader, ctx);
|
||||
var result = await httpClient.GetAsync(new Uri(listenerAddress + "ep"));
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||
var receivedCloudEvent = await result.ToCloudEvent();
|
||||
|
||||
Assert.Equal("0.1", receivedCloudEvent.SpecVersion);
|
||||
Assert.Equal("com.github.pull.create", receivedCloudEvent.Type);
|
||||
Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), receivedCloudEvent.Source);
|
||||
Assert.Equal("A234-1234-1234", receivedCloudEvent.Id);
|
||||
Assert.Equal(DateTime.Parse("2018-04-05T17:31:00Z").ToUniversalTime(),
|
||||
receivedCloudEvent.Time.Value.ToUniversalTime());
|
||||
Assert.Equal(new ContentType(MediaTypeNames.Text.Xml), receivedCloudEvent.ContentType);
|
||||
Assert.Equal("<much wow=\"xml\"/>", receivedCloudEvent.Data);
|
||||
|
||||
var attr = receivedCloudEvent.GetAttributes();
|
||||
Assert.Equal("value", (string)attr["comexampleextension1"]);
|
||||
Assert.Equal(5, (int)((dynamic)attr["comexampleextension2"]).othervalue);
|
||||
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
async Task HttpBinaryClientReceiveTest()
|
||||
{
|
||||
string ctx = Guid.NewGuid().ToString();
|
||||
pendingRequests.TryAdd(ctx, async context =>
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
var cloudEvent = new CloudEvent("com.github.pull.create",
|
||||
new Uri("https://github.com/cloudevents/spec/pull/123"))
|
||||
{
|
||||
Id = "A234-1234-1234",
|
||||
Time = new DateTime(2018, 4, 5, 17, 31, 0, DateTimeKind.Utc),
|
||||
ContentType = new ContentType(MediaTypeNames.Text.Xml),
|
||||
Data = "<much wow=\"xml\"/>"
|
||||
};
|
||||
|
||||
var attrs = cloudEvent.GetAttributes();
|
||||
attrs["comexampleextension1"] = "value";
|
||||
attrs["comexampleextension2"] = new { othervalue = 5 };
|
||||
|
||||
|
||||
await context.Response.CopyFromAsync(cloudEvent, ContentMode.Binary, new JsonEventFormatter());
|
||||
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
using (var sw = new StreamWriter(context.Response.OutputStream))
|
||||
{
|
||||
sw.Write(e.ToString());
|
||||
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
}
|
||||
}
|
||||
context.Response.Close();
|
||||
});
|
||||
|
||||
|
|
@ -294,22 +231,139 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||
var receivedCloudEvent = await result.ToCloudEvent();
|
||||
|
||||
Assert.Equal("0.1", receivedCloudEvent.SpecVersion);
|
||||
Assert.Equal("0.2", receivedCloudEvent.SpecVersion);
|
||||
Assert.Equal("com.github.pull.create", receivedCloudEvent.Type);
|
||||
Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), receivedCloudEvent.Source);
|
||||
Assert.Equal("A234-1234-1234", receivedCloudEvent.Id);
|
||||
Assert.Equal(DateTime.Parse("2018-04-05T17:31:00Z").ToUniversalTime(),
|
||||
receivedCloudEvent.Time.Value.ToUniversalTime());
|
||||
Assert.Equal(new ContentType(MediaTypeNames.Text.Xml), receivedCloudEvent.ContentType);
|
||||
using (var sr = new StreamReader((Stream)receivedCloudEvent.Data))
|
||||
{
|
||||
Assert.Equal("<much wow=\"xml\"/>", sr.ReadToEnd());
|
||||
}
|
||||
Assert.Equal("<much wow=\"xml\"/>", receivedCloudEvent.Data);
|
||||
|
||||
var attr = receivedCloudEvent.GetAttributes();
|
||||
Assert.Equal("value", (string)attr["comexampleextension1"]);
|
||||
Assert.Equal(5, (int)((dynamic)attr["comexampleextension2"]).othervalue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
async Task HttpStructuredClientSendTest()
|
||||
{
|
||||
var cloudEvent = new CloudEvent("com.github.pull.create",
|
||||
new Uri("https://github.com/cloudevents/spec/pull/123"))
|
||||
{
|
||||
Id = "A234-1234-1234",
|
||||
Time = new DateTime(2018, 4, 5, 17, 31, 0, DateTimeKind.Utc),
|
||||
ContentType = new ContentType(MediaTypeNames.Text.Xml),
|
||||
Data = "<much wow=\"xml\"/>"
|
||||
};
|
||||
|
||||
var attrs = cloudEvent.GetAttributes();
|
||||
attrs["comexampleextension1"] = "value";
|
||||
attrs["comexampleextension2"] = new { othervalue = 5 };
|
||||
|
||||
string ctx = Guid.NewGuid().ToString();
|
||||
var content = new CloudEventContent(cloudEvent, ContentMode.Structured, new JsonEventFormatter());
|
||||
content.Headers.Add(testContextHeader, ctx);
|
||||
|
||||
pendingRequests.TryAdd(ctx, async context =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var receivedCloudEvent = await context.Request.ToCloudEventAsync(new JsonEventFormatter());
|
||||
|
||||
Assert.Equal("0.2", receivedCloudEvent.SpecVersion);
|
||||
Assert.Equal("com.github.pull.create", receivedCloudEvent.Type);
|
||||
Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), receivedCloudEvent.Source);
|
||||
Assert.Equal("A234-1234-1234", receivedCloudEvent.Id);
|
||||
Assert.Equal(DateTime.Parse("2018-04-05T17:31:00Z").ToUniversalTime(),
|
||||
receivedCloudEvent.Time.Value.ToUniversalTime());
|
||||
Assert.Equal(new ContentType(MediaTypeNames.Text.Xml), receivedCloudEvent.ContentType);
|
||||
Assert.Equal("<much wow=\"xml\"/>", receivedCloudEvent.Data);
|
||||
|
||||
var attr = receivedCloudEvent.GetAttributes();
|
||||
Assert.Equal("value", (string)attr["comexampleextension1"]);
|
||||
Assert.Equal(5, (int)((dynamic)attr["comexampleextension2"]).othervalue);
|
||||
context.Response.StatusCode = (int)HttpStatusCode.NoContent;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
using (var sw = new StreamWriter(context.Response.OutputStream))
|
||||
{
|
||||
sw.Write(e.ToString());
|
||||
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.Close();
|
||||
});
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
var result = (await httpClient.PostAsync(new Uri(listenerAddress + "ep"), content));
|
||||
if (result.StatusCode != HttpStatusCode.NoContent)
|
||||
{
|
||||
throw new InvalidOperationException(result.Content.ReadAsStringAsync().GetAwaiter().GetResult());
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
async Task HttpStructuredWebRequestSendTest()
|
||||
{
|
||||
var cloudEvent = new CloudEvent("com.github.pull.create",
|
||||
new Uri("https://github.com/cloudevents/spec/pull/123"))
|
||||
{
|
||||
Id = "A234-1234-1234",
|
||||
Time = new DateTime(2018, 4, 5, 17, 31, 0, DateTimeKind.Utc),
|
||||
ContentType = new ContentType(MediaTypeNames.Text.Xml),
|
||||
Data = "<much wow=\"xml\"/>"
|
||||
};
|
||||
|
||||
var attrs = cloudEvent.GetAttributes();
|
||||
attrs["comexampleextension1"] = "value";
|
||||
attrs["comexampleextension2"] = new { othervalue = 5 };
|
||||
|
||||
string ctx = Guid.NewGuid().ToString();
|
||||
HttpWebRequest httpWebRequest = WebRequest.CreateHttp(listenerAddress + "ep");
|
||||
httpWebRequest.Method = "POST";
|
||||
await httpWebRequest.CopyFromAsync(cloudEvent, ContentMode.Structured, new JsonEventFormatter());
|
||||
httpWebRequest.Headers.Add(testContextHeader, ctx);
|
||||
|
||||
pendingRequests.TryAdd(ctx, async context =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var receivedCloudEvent = await context.Request.ToCloudEventAsync(new JsonEventFormatter());
|
||||
|
||||
Assert.Equal("0.2", receivedCloudEvent.SpecVersion);
|
||||
Assert.Equal("com.github.pull.create", receivedCloudEvent.Type);
|
||||
Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), receivedCloudEvent.Source);
|
||||
Assert.Equal("A234-1234-1234", receivedCloudEvent.Id);
|
||||
Assert.Equal(DateTime.Parse("2018-04-05T17:31:00Z").ToUniversalTime(),
|
||||
receivedCloudEvent.Time.Value.ToUniversalTime());
|
||||
Assert.Equal(new ContentType(MediaTypeNames.Text.Xml), receivedCloudEvent.ContentType);
|
||||
Assert.Equal("<much wow=\"xml\"/>", receivedCloudEvent.Data);
|
||||
|
||||
var attr = receivedCloudEvent.GetAttributes();
|
||||
Assert.Equal("value", (string)attr["comexampleextension1"]);
|
||||
Assert.Equal(5, (int)((dynamic)attr["comexampleextension2"]).othervalue);
|
||||
context.Response.StatusCode = (int)HttpStatusCode.NoContent;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
using (var sw = new StreamWriter(context.Response.OutputStream))
|
||||
{
|
||||
sw.Write(e.ToString());
|
||||
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.Close();
|
||||
});
|
||||
|
||||
var result = (HttpWebResponse)await httpWebRequest.GetResponseAsync();
|
||||
if (result.StatusCode != HttpStatusCode.NoContent)
|
||||
{
|
||||
throw new InvalidOperationException(result.StatusCode.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
{
|
||||
const string json =
|
||||
"{\n" +
|
||||
" \"specversion\" : \"0.1\",\n" +
|
||||
" \"specversion\" : \"0.2\",\n" +
|
||||
" \"type\" : \"com.github.pull.create\",\n" +
|
||||
" \"source\" : \"https://github.com/cloudevents/spec/pull/123\",\n" +
|
||||
" \"id\" : \"A234-1234-1234\",\n" +
|
||||
|
|
@ -48,7 +48,7 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
{
|
||||
var jsonFormatter = new JsonEventFormatter();
|
||||
var cloudEvent = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(json));
|
||||
Assert.Equal("0.1", cloudEvent.SpecVersion);
|
||||
Assert.Equal("0.2", cloudEvent.SpecVersion);
|
||||
Assert.Equal("com.github.pull.create", cloudEvent.Type);
|
||||
Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), cloudEvent.Source);
|
||||
Assert.Equal("A234-1234-1234", cloudEvent.Id);
|
||||
|
|
@ -68,7 +68,7 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
var jsonFormatter = new JsonEventFormatter();
|
||||
var cloudEvent = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(json), new ComExampleExtension1Extension(),
|
||||
new ComExampleExtension2Extension());
|
||||
Assert.Equal("0.1", cloudEvent.SpecVersion);
|
||||
Assert.Equal("0.2", cloudEvent.SpecVersion);
|
||||
Assert.Equal("com.github.pull.create", cloudEvent.Type);
|
||||
Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), cloudEvent.Source);
|
||||
Assert.Equal("A234-1234-1234", cloudEvent.Id);
|
||||
|
|
|
|||
Loading…
Reference in New Issue