Skip to content

Commit

Permalink
Add System.Net.ServerSentEvents
Browse files Browse the repository at this point in the history
  • Loading branch information
stephentoub committed May 29, 2024
1 parent 8b1d06a commit 7f83e8b
Show file tree
Hide file tree
Showing 12 changed files with 1,954 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.34910.147
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.ServerSentEvents", "ref\System.Net.ServerSentEvents.csproj", "{ACB7E0BF-015F-43DC-A2F5-85506173B223}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.ServerSentEvents", "src\System.Net.ServerSentEvents.csproj", "{ACDB56AF-7B9F-4762-9764-D6FF09118D09}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.ServerSentEvents.Tests", "tests\System.Net.ServerSentEvents.Tests.csproj", "{804B5D44-05A3-491E-A6AB-35C592E6703E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{2BD73108-47D7-40E6-BFCB-169E6AD42A81}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{E6AF8CEE-6550-4190-97D4-D51C5B114919}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D908DCBE-EFA4-4CCA-9A1C-AEB48D59C504}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ACB7E0BF-015F-43DC-A2F5-85506173B223}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ACB7E0BF-015F-43DC-A2F5-85506173B223}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ACB7E0BF-015F-43DC-A2F5-85506173B223}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ACB7E0BF-015F-43DC-A2F5-85506173B223}.Release|Any CPU.Build.0 = Release|Any CPU
{ACDB56AF-7B9F-4762-9764-D6FF09118D09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ACDB56AF-7B9F-4762-9764-D6FF09118D09}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ACDB56AF-7B9F-4762-9764-D6FF09118D09}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ACDB56AF-7B9F-4762-9764-D6FF09118D09}.Release|Any CPU.Build.0 = Release|Any CPU
{804B5D44-05A3-491E-A6AB-35C592E6703E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{804B5D44-05A3-491E-A6AB-35C592E6703E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{804B5D44-05A3-491E-A6AB-35C592E6703E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{804B5D44-05A3-491E-A6AB-35C592E6703E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{ACB7E0BF-015F-43DC-A2F5-85506173B223} = {E6AF8CEE-6550-4190-97D4-D51C5B114919}
{ACDB56AF-7B9F-4762-9764-D6FF09118D09} = {D908DCBE-EFA4-4CCA-9A1C-AEB48D59C504}
{804B5D44-05A3-491E-A6AB-35C592E6703E} = {2BD73108-47D7-40E6-BFCB-169E6AD42A81}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {01DAF96B-AF8E-4576-A1BC-57D19BDB317E}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// ------------------------------------------------------------------------------
// Changes to this file must follow the https://aka.ms/api-review process.
// ------------------------------------------------------------------------------

namespace System.Net.ServerSentEvents
{
public delegate T SseItemParser<out T>(string eventType, System.ReadOnlySpan<byte> data);
public readonly partial struct SseItem<T>
{
private readonly T _Data_k__BackingField;
private readonly object _dummy;
private readonly int _dummyPrimitive;
public SseItem(T data, string eventType) { throw null; }
public T Data { get { throw null; } }
public string EventType { get { throw null; } }
}
public static partial class SseParser
{
public const string EventTypeDefault = "message";
public static System.Net.ServerSentEvents.SseParser<string> Create(System.IO.Stream sseStream) { throw null; }
public static System.Net.ServerSentEvents.SseParser<T> Create<T>(System.IO.Stream sseStream, System.Net.ServerSentEvents.SseItemParser<T> itemParser) { throw null; }
}
public sealed partial class SseParser<T>
{
internal SseParser() { }
public string LastEventId { get { throw null; } }
public System.TimeSpan ReconnectionInterval { get { throw null; } }
public System.Collections.Generic.IEnumerable<System.Net.ServerSentEvents.SseItem<T>> Enumerate() { throw null; }
public System.Collections.Generic.IAsyncEnumerable<System.Net.ServerSentEvents.SseItem<T>> EnumerateAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum)</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<Compile Include="System.Net.ServerSentEvents.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
<PackageReference Include="System.Memory" Version="$(SystemMemoryVersion)" />
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Bcl.AsyncInterfaces\ref\Microsoft.Bcl.AsyncInterfaces.csproj" />
</ItemGroup>

</Project>
52 changes: 52 additions & 0 deletions src/libraries/System.Net.ServerSentEvents/src/PACKAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
## About

<!-- A description of the package and where one can find more documentation -->

System.Net.ServerSentEvents provides the `SseParser` type, which exposes factory methods for creating parsers for the events in a stream of server-sent events (SSE).

## Key Features

<!-- The key features of this package -->

* Parser for server-sent events (SSE)

## How to Use

<!-- A compelling example on how to use this package with code, as well as any specific guidelines for when to use the package -->

Asynchronously parsing event contents as strings

```csharp
using HttpClient client = new();
using Stream response = await client.GetStreamAsync("https://localhost:12345/sse");
await foreach (SseItem<string> item in SseParser.Create(response).EnumerateAsync())
{
Console.WriteLine(item.Data);
}
```

Synchronously parsing event contents as JSON

```csharp
MemoryStream stream = new(data);
foreach (SseItem<Book> item in SseParser.Create(response, (eventType, bytes) => JsonSerializer.Deserialize<Book>(bytes)).Enumerate())
{
Console.WriteLine(item.Author);
}
```

## Main Types

<!-- The main types provided in this library -->

The main types provided by this library are:

* `System.Net.ServerSentEvents.SseParser`
* `System.Net.ServerSentEvents.SseParser<T>`
* `System.Net.ServerSentEvents.SseItem<T>`

## Feedback & Contributing

<!-- How to provide feedback on this package and contribute to it -->

System.Net.ServerSentEvents is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime).
126 changes: 126 additions & 0 deletions src/libraries/System.Net.ServerSentEvents/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="InvalidOperation_EnumerateOnlyOnce" xml:space="preserve">
<value>The enumerable may be enumerated only once.</value>
</data>
<data name="InvalidOperation_InvalidStreamNegativeBytes" xml:space="preserve">
<value>The Stream implementation is invalid, returning a negative number from a read operation.</value>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum)</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<UseCompilerGeneratedDocXmlFile>true</UseCompilerGeneratedDocXmlFile>
<IsPackable>true</IsPackable>
<PackageDescription>Provides a simple parser for server-sent events (SSE).

Commonly Used Types:
System.Net.ServerSentEvents.SseParser</PackageDescription>

<!-- Disabling baseline validation since this is a brand new package.
Once this package has shipped a stable version, the following line
should be removed in order to re-enable validation. -->
<DisablePackageBaselineValidation>true</DisablePackageBaselineValidation>
</PropertyGroup>

<ItemGroup>
<Compile Include="System\Net\ServerSentEvents\SseParser_1.cs" />
<Compile Include="System\Net\ServerSentEvents\SseItem.cs" />
<Compile Include="System\Net\ServerSentEvents\SseItemParser.cs" />
<Compile Include="System\Net\ServerSentEvents\SseParser.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Bcl.AsyncInterfaces\src\Microsoft.Bcl.AsyncInterfaces.csproj" />
<PackageReference Include="System.Memory" Version="$(SystemMemoryVersion)" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="$(SystemThreadingTasksExtensionsVersion)" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Net.ServerSentEvents
{
/// <summary>Represents a server-sent event.</summary>
/// <typeparam name="T">Specifies the type of data payload in the event.</typeparam>
public readonly struct SseItem<T>
{
/// <summary>Initializes the server-sent event.</summary>
/// <param name="data">The event's payload.</param>
/// <param name="eventType">The event's type.</param>
public SseItem(T data, string eventType)
{
Data = data;
EventType = eventType;
}

/// <summary>Gets the event's payload.</summary>
public T Data { get; }

/// <summary>Gets the event's type.</summary>
public string EventType { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Net.ServerSentEvents
{
/// <summary>Encapsulates a method for parsing the bytes payload of a server-sent event.</summary>
/// <typeparam name="T">Specifies the type of the return value of the parser.</typeparam>
/// <param name="eventType">The event's type.</param>
/// <param name="data">The event's payload bytes.</param>
/// <returns>The parsed <typeparamref name="T"/>.</returns>
public delegate T SseItemParser<out T>(string eventType, ReadOnlySpan<byte> data);
}
Loading

0 comments on commit 7f83e8b

Please sign in to comment.