From 59faa1d150ecdb27d31c1892612f5ea73b76cf28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Tue, 18 May 2021 14:06:50 +0200 Subject: [PATCH 1/3] Setup docfx --- api-doc/.gitignore | 9 ++++++ api-doc/api/.gitignore | 5 +++ api-doc/api/index.md | 2 ++ api-doc/articles/intro.md | 1 + api-doc/articles/toc.yml | 2 ++ api-doc/docfx.json | 64 +++++++++++++++++++++++++++++++++++++++ api-doc/index.md | 4 +++ api-doc/toc.yml | 5 +++ 8 files changed, 92 insertions(+) create mode 100644 api-doc/.gitignore create mode 100644 api-doc/api/.gitignore create mode 100644 api-doc/api/index.md create mode 100644 api-doc/articles/intro.md create mode 100644 api-doc/articles/toc.yml create mode 100644 api-doc/docfx.json create mode 100644 api-doc/index.md create mode 100644 api-doc/toc.yml diff --git a/api-doc/.gitignore b/api-doc/.gitignore new file mode 100644 index 0000000..4378419 --- /dev/null +++ b/api-doc/.gitignore @@ -0,0 +1,9 @@ +############### +# folder # +############### +/**/DROP/ +/**/TEMP/ +/**/packages/ +/**/bin/ +/**/obj/ +_site diff --git a/api-doc/api/.gitignore b/api-doc/api/.gitignore new file mode 100644 index 0000000..e8079a3 --- /dev/null +++ b/api-doc/api/.gitignore @@ -0,0 +1,5 @@ +############### +# temp file # +############### +*.yml +.manifest diff --git a/api-doc/api/index.md b/api-doc/api/index.md new file mode 100644 index 0000000..78dc9c0 --- /dev/null +++ b/api-doc/api/index.md @@ -0,0 +1,2 @@ +# PLACEHOLDER +TODO: Add .NET projects to the *src* folder and run `docfx` to generate **REAL** *API Documentation*! diff --git a/api-doc/articles/intro.md b/api-doc/articles/intro.md new file mode 100644 index 0000000..c0478ce --- /dev/null +++ b/api-doc/articles/intro.md @@ -0,0 +1 @@ +# Add your introductions here! diff --git a/api-doc/articles/toc.yml b/api-doc/articles/toc.yml new file mode 100644 index 0000000..ff89ef1 --- /dev/null +++ b/api-doc/articles/toc.yml @@ -0,0 +1,2 @@ +- name: Introduction + href: intro.md diff --git a/api-doc/docfx.json b/api-doc/docfx.json new file mode 100644 index 0000000..5cd1d48 --- /dev/null +++ b/api-doc/docfx.json @@ -0,0 +1,64 @@ +{ + "metadata": [ + { + "src": [ + { + "files": [ + "src/**.csproj" + ] + } + ], + "dest": "api", + "disableGitFeatures": false, + "disableDefaultFilter": false + } + ], + "build": { + "content": [ + { + "files": [ + "api/**.yml", + "api/index.md" + ] + }, + { + "files": [ + "articles/**.md", + "articles/**/toc.yml", + "toc.yml", + "*.md" + ] + } + ], + "resource": [ + { + "files": [ + "images/**" + ] + } + ], + "overwrite": [ + { + "files": [ + "apidoc/**.md" + ], + "exclude": [ + "obj/**", + "_site/**" + ] + } + ], + "dest": "_site", + "globalMetadataFiles": [], + "fileMetadataFiles": [], + "template": [ + "default" + ], + "postProcessors": [], + "markdownEngineName": "markdig", + "noLangKeyword": false, + "keepFileLink": false, + "cleanupCacheHistory": false, + "disableGitFeatures": false + } +} \ No newline at end of file diff --git a/api-doc/index.md b/api-doc/index.md new file mode 100644 index 0000000..3ae2506 --- /dev/null +++ b/api-doc/index.md @@ -0,0 +1,4 @@ +# This is the **HOMEPAGE**. +Refer to [Markdown](http://daringfireball.net/projects/markdown/) for how to write markdown files. +## Quick Start Notes: +1. Add images to the *images* folder if the file is referencing an image. diff --git a/api-doc/toc.yml b/api-doc/toc.yml new file mode 100644 index 0000000..59f8010 --- /dev/null +++ b/api-doc/toc.yml @@ -0,0 +1,5 @@ +- name: Articles + href: articles/ +- name: Api Documentation + href: api/ + homepage: api/index.md From 7448739ebdfc19433cb557f6405aeb90a0ccf859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Tue, 18 May 2021 14:13:01 +0200 Subject: [PATCH 2/3] Adjustments for docfx --- api-doc/docfx.json | 125 ++++++++++++++++++----------------- api-doc/index.md | 161 +++++++++++++++++++++++++++++++++++++++++++-- api-doc/toc.yml | 6 +- 3 files changed, 226 insertions(+), 66 deletions(-) diff --git a/api-doc/docfx.json b/api-doc/docfx.json index 5cd1d48..f7953a1 100644 --- a/api-doc/docfx.json +++ b/api-doc/docfx.json @@ -1,64 +1,71 @@ { - "metadata": [ - { - "src": [ + "metadata": [ { - "files": [ - "src/**.csproj" - ] + "src": [ + { + "cwd": "../", + "files": [ + "src/**.csproj" + ] + } + ], + "dest": "api", + "disableGitFeatures": false, + "disableDefaultFilter": false } - ], - "dest": "api", - "disableGitFeatures": false, - "disableDefaultFilter": false - } - ], - "build": { - "content": [ - { - "files": [ - "api/**.yml", - "api/index.md" - ] - }, - { - "files": [ - "articles/**.md", - "articles/**/toc.yml", - "toc.yml", - "*.md" - ] - } - ], - "resource": [ - { - "files": [ - "images/**" - ] - } ], - "overwrite": [ - { - "files": [ - "apidoc/**.md" + "build": { + "content": [ + { + "files": [ + "api/**.yml", + "api/index.md" + ] + }, + { + "files": [ + "articles/**.md", + "articles/**/toc.yml", + "toc.yml", + "*.md" + ] + } ], - "exclude": [ - "obj/**", - "_site/**" - ] - } - ], - "dest": "_site", - "globalMetadataFiles": [], - "fileMetadataFiles": [], - "template": [ - "default" - ], - "postProcessors": [], - "markdownEngineName": "markdig", - "noLangKeyword": false, - "keepFileLink": false, - "cleanupCacheHistory": false, - "disableGitFeatures": false - } -} \ No newline at end of file + "resource": [ + { + "files": [ + "images/**" + ] + } + ], + "overwrite": [ + { + "files": [ + "apidoc/**.md" + ], + "exclude": [ + "obj/**", + "_site/**" + ] + } + ], + "globalMetadata": { + "_appTitle": "MyCSharp.HttpUserAgentParser", + "_appFooter": "Copyright © MyCSharp 2021", + "_appLogoPath": "null", + "_appFaviconPath": "null" + }, + "dest": "_site", + "globalMetadataFiles": [], + "fileMetadataFiles": [], + "template": [ + "default" + ], + "postProcessors": [], + "markdownEngineName": "markdig", + "noLangKeyword": false, + "keepFileLink": false, + "cleanupCacheHistory": false, + "disableGitFeatures": false + } +} diff --git a/api-doc/index.md b/api-doc/index.md index 3ae2506..712f02e 100644 --- a/api-doc/index.md +++ b/api-doc/index.md @@ -1,4 +1,157 @@ -# This is the **HOMEPAGE**. -Refer to [Markdown](http://daringfireball.net/projects/markdown/) for how to write markdown files. -## Quick Start Notes: -1. Add images to the *images* folder if the file is referencing an image. +# MyCSharp.HttpUserAgentParser + +Parsing HTTP User Agents with .NET + +## NuGet + +| NuGet | +|-| +| [![MyCSharp.HttpUserAgentParser](https://img.shields.io/nuget/v/MyCSharp.HttpUserAgentParser.svg?logo=nuget&label=MyCSharp.HttpUserAgentParser)](https://www.nuget.org/packages/MyCSharp.HttpUserAgentParser) | +| [![MyCSharp.HttpUserAgentParser](https://img.shields.io/nuget/v/MyCSharp.HttpUserAgentParser.MemoryCache.svg?logo=nuget&label=MyCSharp.HttpUserAgentParser.MemoryCache)](https://www.nuget.org/packages/MyCSharp.HttpUserAgentParser.MemoryCache)| `dotnet add package MyCSharp.HttpUserAgentParser.MemoryCach.MemoryCache` | +| [![MyCSharp.HttpUserAgentParser.AspNetCore](https://img.shields.io/nuget/v/MyCSharp.HttpUserAgentParser.AspNetCore.svg?logo=nuget&label=MyCSharp.HttpUserAgentParser.AspNetCore)](https://www.nuget.org/packages/MyCSharp.HttpUserAgentParser.AspNetCore) | `dotnet add package MyCSharp.HttpUserAgentParser.AspNetCore` | + + +## Usage + +```csharp +string userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"; +HttpUserAgentInformation info = HttpUserAgentParser.Parse(userAgent); // alias HttpUserAgentInformation.Parse() +``` +returns +```csharp +UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36" +Type = HttpUserAgentType.Browser +Platform = { + Name = "Windows 10", + PlatformType = HttpUserAgentPlatformType.Windows +} +Name = "Chrome" +Version = "90.0.4430.212" +MobileDeviceType = null +``` + +### Dependency Injection and Caching + +For dependency injection mechanisms, the `IHttpUserAgentParserProvider` interface exists, for which built-in or custom caching mechanisms can be used. The use is always: + +```csharp +private IHttpUserAgentParserProvider _parser; +public void MyMethod(string userAgent) +{ + HttpUserAgentInformation info = _parser.Parse(userAgent); +} +``` + +If no cache is required but dependency injection is still desired, the default cache provider can simply be used. This registers the `HttpUserAgentParserDefaultProvider`, which does not cache at all. + +```csharp +public void ConfigureServices(IServiceCollection services) +{ + services.AddHttpUserAgentParser(); // uses HttpUserAgentParserDefaultProvider and does not cache +} +``` + +Likewise, an In Process Cache mechanism is provided, based on a `ConcurrentDictionary`. + +```csharp +public void ConfigureServices(IServiceCollection services) +{ + services.AddHttpUserAgentCachedParser(); // uses `HttpUserAgentParserCachedProvider` + // or + // services.AddHttpUserAgentParser(); +} +``` + + This is especially recommended for tests. For web applications, the `IMemoryCache` implementation should be used, which offers a timed expiration of the entries. + +The package [MyCSharp.HttpUserAgentParser.MemoryCache](https://www.nuget.org/packages/MyCSharp.HttpUserAgentParser.MemoryCache) is required to use the IMemoryCache. This enables the registration of the `IMemoryCache` implementation: + + +```csharp +public void ConfigureServices(IServiceCollection services) +{ + services.AddHttpUserAgentMemoryCachedParser(); + + // or use options + + services.AddHttpUserAgentMemoryCachedParser(options => + { + options.CacheEntryOptions.SlidingExpiration = TimeSpan.FromMinutes(60); // default is 1 day + + // limit the total entries in the MemoryCache + // each unique user agent string counts as one entry + options.CacheOptions.SizeLimit = 1024; // default is 256 + }); +} +``` + +### ASP.NET Core + +For ASP.NET Core applications, an accessor pattern (`IHttpUserAgentParserAccessor`) implementation can be registered additionally that independently retrieves the user agent based on the `HttpContextAccessor`. This requires the package [MyCSharp.HttpUserAgentParser.AspNetCore](https://www.nuget.org/packages/MyCSharp.HttpUserAgentParser.AspNetCore) + +```csharp +public void ConfigureServices(IServiceCollection services) +{ + services.AddHttpUserAgentParserAccessor(); // registers IHttpUserAgentParserAccessor +} +``` + +Now you can use + +```csharp +public void MyMethod(IHttpUserAgentParserAccessor parserAccessor) +{ + HttpUserAgentInformation info = parserAccessor.Get(); +} +``` + +## Benchmark + +```sh +BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042 +AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores +.NET Core SDK=5.0.300-preview.21228.15 + [Host] : .NET Core 5.0.5 (CoreCLR 5.0.521.16609, CoreFX 5.0.521.16609), X64 RyuJIT + DefaultJob : .NET Core 5.0.5 (CoreCLR 5.0.521.16609, CoreFX 5.0.521.16609), X64 RyuJIT +``` + +| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | +|-------------------- |------------:|----------:|----------:|--------:|-------:|------:|----------:| +| 'UA Parser' | 3,238.59 us | 27.435 us | 25.663 us | 7.8125 | - | - | 168225 B | +| UserAgentService | 391.11 us | 5.126 us | 4.795 us | 35.1563 | 3.4180 | - | 589664 B | +| HttpUserAgentParser | 67.07 us | 0.740 us | 0.693 us | - | - | - | 848 B | + +More benchmark results can be found [in this comment](https://github.com/mycsharp/HttpUserAgentParser/issues/2#issuecomment-842188532). + +## Disclaimer + +This library is inspired by [UserAgentService by DannyBoyNg](https://github.com/DannyBoyNg/UserAgentService) and contains optimizations for our requirements on [myCSharp.de](https://mycsharp.de). +We decided to fork the project, because we want a general restructuring with corresponding breaking changes. + +## Maintained + +by [@BenjaminAbt](https://github.com/BenjaminAbt) and [@gfoidl](https://github.com/gfoidl) + +## License + +MIT License + +Copyright (c) 2021 MyCSharp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/api-doc/toc.yml b/api-doc/toc.yml index 59f8010..27a6c26 100644 --- a/api-doc/toc.yml +++ b/api-doc/toc.yml @@ -1,5 +1,5 @@ -- name: Articles - href: articles/ +#- name: Articles +# href: articles/ - name: Api Documentation href: api/ - homepage: api/index.md + #homepage: api/index.md From c70a8b5f3f2b62adad320fff23db8950e4534f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Tue, 18 May 2021 14:57:06 +0200 Subject: [PATCH 3/3] CI/CD --- .github/workflows/ci.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0f3e00..58dfad5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,3 +45,15 @@ jobs: --api-key ${{ secrets.NUGET_DEPLOY_KEY }} --source https://api.nuget.org/v3/index.json --no-symbols 1 + + - uses: nikeee/docfx-action@v1.0.0 + name: Build Documentation + #if: github.ref == 'refs/heads/main' commented to test CI + with: + args: api-doc/docfx.json + + # - uses: maxheld83/ghpages@master + # name: Publish Documentation on GitHub Pages + # env: + # BUILD_DIR: api-doc/_site # docfx's default output directory is _site + # GH_PAT: ${{ secrets.GH_PAT }} # See https://github.com/maxheld83/ghpages