Skip to content

Develop 2.0.0-alpha - #19

Merged
ReneSchwarzer merged 99 commits into
mainfrom
develop
Oct 31, 2026
Merged

ReneSchwarzer merged 99 commits into
mainfrom
develop

Conversation

@ReneSchwarzer

Copy link
Copy Markdown
Member

This pull request introduces several documentation improvements, test fixture enhancements, and minor code corrections to the WebExpress project. The most significant updates include expanded documentation on health checks, graceful shutdown, and server entry points, as well as the addition of new health check test fixtures. There are also corrections to the mock identity group policy handling and minor typo and link fixes.

Documentation improvements:

  • Expanded the docs/user-guide.md with detailed sections on health checks (including endpoint, component model, and probe configuration), graceful shutdown, public server URI configuration, and server entry point behavior. This provides clearer operational guidance for users and developers.
  • Updated documentation links to point to the correct docs/ directory and fixed minor typos (e.g., "Raspberry PI" to "Raspberry Pi", ".net" to ".NET"). Added an AI transparency notice to the README.md. [1] [2] [3] [4]

Health check and test fixture enhancements:

  • Added new test health components in TestHealth.cs, including sealed and generic health components to test discovery, dependency injection, and attribute-driven cancellation. This ensures the health check infrastructure is robust and testable.
  • Updated the documentation and table of contents to include direct links to the health model section.

Mock identity and policy handling corrections:

  • Refactored MockIdentityGroup in test code to use IIdentityPolicy objects instead of string role names for policy assignment and enumeration, aligning with intended usage. [1] [2] [3] [4]
  • Fixed a method name in MockIdentityProvider from CreateForbiddenPage to CreateForbiddenResponse for accuracy and clarity.

ReneSchwarzer and others added 29 commits October 18, 2026 21:58
HEAD and POST ignore the Range header and keep the full representation
metadata (length, type, ETag) instead of answering with a partial body.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Content of script, style, title, textarea, xmp, iframe, noembed, noframes
and plaintext is no longer split into tags; it ends only at the matching
closing tag (plaintext never ends).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The SMTP provider now talks to the server over .NET sockets and TLS
streams and serializes MIME itself (UTF-8 headers, base64 bodies and
attachments, extended filename parameters). IEmailProvider receives a
System.Net.Mail.MailMessage instead of a MimeKit message, so the MailKit
package reference is dropped. Authentication supports PLAIN and LOGIN
over TLS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The OpenID Connect authorize endpoint accepts local returnUrl and
failureUrl destinations, binds them to the challenge and redirects the
browser after the callback; failures return a stable wx-auth-error code
instead of provider error text. Login takes a rememberMe flag; without
it the identity cookies live only for the browser session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Asset addresses carry no version, yet assets were served with
"public, max-age=31536000": a browser kept outdated scripts and
stylesheets after every deployment, even across a normal reload. They
are now "no-cache" - kept, but revalidated, which the existing entity
tag answers with a body-less 304.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both credentials are http-only, so a page could not tell whether a grant
exists or when the access credential runs out. Login and refresh now set
a readable hint cookie (__Host-wx-auth-renew, wx-auth-renew over HTTP)
holding "<applicationId>:<access deadline>" - no credential - with the
lifetime of the refresh cookie.

A refused renewal expires all authentication cookies, the hint among
them, so a script does not ask again on every page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Names keep their first letters and initials, so the sort and initials
assertions still describe the same orderings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…RTY-NOTICES.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Microsoft.Sbom.Targets embeds the manifest in the nupkg; a copy is
placed next to the package as <packageid>.spdx.json. Visual Studio
builds hand the generation to the dotnet msbuild, since the bundled
sbom-tool.exe requires the .NET 8 runtime.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every change goes through the public PackageManager operations: uploads
are checked in a staging directory before they replace anything, a start
that fails is rolled back, a failed update restores the previous version,
and dependencies are resolved in both directions - waiting packages start
with what they need, deactivating a package stops its dependents until it
is back. A plugin id that is already taken is refused instead of handing
out the registered plugin's context.

Plugins shipping with the application can be deactivated, activated and
uninstalled, except system plugins. Their state is kept in the catalog;
an uninstalled plugin's assembly is deleted on the next start, before the
plugins load, unless the host's dependency manifest lists it.

Messages and the reason a package does not run are kept as key and
arguments and translated for the reader, not in the server's culture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rations see unscanned packages

Packages record the settings files they actually deployed (also in the shared
cluster state), so an archive can no longer claim a host or foreign plugin
configuration on removal. Cluster operations run a full shared-only scan first.
SessionManager.FindSession loads the current state of an existing session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dotnet test only accepts --project and --report-trx in Microsoft.Testing.Platform
mode, which global.json switches on. Since the repo ships without one, the
workflow writes it before running the tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every test boots a component hub and stored it in the process-wide
WebEx._componentHub, so the whole assembly had to run one test at a time.
WebEx now prefers a hub bound to the current call chain (an async local),
which the fixture sets; the server never binds one and keeps answering with
the process-wide hub.

- the shared i18n table and the fragment delegate cache become concurrent
  dictionaries: every hub fills them, and a plugin loaded at runtime fills
  them while requests read them
- UnitTestFixture.BindComponentHub replaces the reflection writes of the
  tests that simulate a missing hub
- the assembly-wide serial mode is dropped; only tests on process-wide state
  (server, ports, files, cluster stores, log, culture) or on short timeouts
  stay in the NonParallelTests collection

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A hub registers every embedded asset of every plugin - about 2000 for
WebExpress.WebUI - and booting one took 134 ms, most of it per asset.

- an asset reads its payload and computes its entity tag on the first
  request instead of at registration
- ComponentActivator looks up constructors, hub properties and interfaces
  once per type, in weak tables so a plugin's load context stays
  unloadable, and resolves the parameters of its hot overload in a loop
- AssetItemDictionary keys the assets by endpoint id instead of scanning
  the list for a duplicate on every add
- RouteEndpoint takes its segments in once instead of keeping a deferred
  query that copied every segment again on each read
- AssetManager builds the asset prefix once per application
- the i18n resource filter compares ordinally; the culture-aware comparison
  was slow and wrong for identifiers under e.g. a turkish culture

A hub now starts in 35 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… meta values

- a websocket upgrade now passes the endpoint's access check before the
  handshake; a socket with policies no longer accepts anonymous clients
- ResourceFile refuses paths that resolve outside RootDirectory - dot
  segments with either separator, and urls that only share the route as a
  prefix (/files-sibling) and landed in a sibling directory
- HtmlElementMetadataMeta writes its name and value through the attribute
  escaping, so a quote in e.g. a description cannot inject markup into the
  head; meta attributes are now double-quoted

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The host joins APP_CONTEXT_DEPS_FILES with ';' regardless of the OS. Splitting
on Path.PathSeparator (':' on Linux) produced one nonexistent path, which the
unreadable-manifest guard treated as listing every file, so removed built-in
plugins were never deleted on start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nager

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…of failing with 500

The socket endpoints registered no request handler, so a GET without the WebSocket
handshake reached the endpoint dispatch and threw a NullReferenceException. It now
answers 426 Upgrade Required with "Upgrade: websocket".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…json

The unit test workflow wrote the file at run time because the repo shipped without one; it
now finds the committed one from src.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ReneSchwarzer
ReneSchwarzer merged commit ab059e4 into main Oct 31, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants