Skip to content

Add client-side WebHttpBinding support (closes #1413, #46)#5959

Open
afifi-ins wants to merge 16 commits into
dotnet:mainfrom
afifi-ins:feature/webhttpbinding-porting
Open

Add client-side WebHttpBinding support (closes #1413, #46)#5959
afifi-ins wants to merge 16 commits into
dotnet:mainfrom
afifi-ins:feature/webhttpbinding-porting

Conversation

@afifi-ins

Copy link
Copy Markdown
Contributor

Closes #1413
Closes #46

Summary

Adds client-side WebHttpBinding support to .NET (Core) WCF. The entire System.ServiceModel.Web namespace (binding, behavior, attributes, WebChannelFactory<T>, UriTemplate, WebOperationContext, QueryStringConverter, ...) has been absent from dotnet/wcf since the start — confirmed missing in both dotnet/wcf and dotnet/runtime (the runtime ships a System.ServiceModel.Web shim that only forwards Syndication/JSON types — zero WCF REST types). Issues #1413 and #46 have tracked this gap since 2015–2016.

This PR introduces a new src/System.ServiceModel.Web/ package that ships ~14,000 LOC of client-side REST support, sourced from:

  • CoreWCF.WebHttp v1.8.0 (MIT, .NET Foundation) — encoder factories, attributes, contexts, behavior, dispatcher formatters
  • Microsoft .NET Framework Reference Source mirror in mono/mono (MIT since 2014) — the full UriTemplate cluster and WebChannelFactory<T> (CoreWCF doesn't ship these because they're either standalone utilities or strictly client-only)

What ships

Namespace Public types
System UriTemplate, UriTemplateTable, UriTemplateMatch, UriTemplateMatchException, UriTemplateEquivalenceComparer
System.ServiceModel WebHttpBinding, WebHttpSecurity, WebHttpSecurityMode
System.ServiceModel.Channels WebMessageEncodingBindingElement, WebContentTypeMapper, WebBodyFormatMessageProperty, WebContentFormat
System.ServiceModel.Description WebHttpBehavior (~870 LOC)
System.ServiceModel.Dispatcher QueryStringConverter, UriTemplateClientFormatter, SingleBodyParameter{,DataContract,XmlSerializer}MessageFormatter, DataContractJsonSerializerOperationFormatter, HttpStreamFormatter, {Xml,Json,Multiplexing}FormatMapping, UnwrappedTypesXmlSerializerManager, NameValueCache
System.ServiceModel.Web WebGetAttribute, WebInvokeAttribute, WebChannelFactory<T>, WebFaultException, WebFaultException<T>, WebOperationContext, IncomingWebRequestContext, IncomingWebResponseContext, OutgoingWebRequestContext, OutgoingWebResponseContext, WebMessageFormat, WebMessageBodyStyle

Surgically deferred (NotSupportedException with clear messages)

dotnet/wcf is a client-only library, so the following server-side features are explicitly stubbed and documented per commit. Use CoreWCF.WebHttp on the server side:

  • WebHttpBehavior.ApplyDispatchBehavior (and the helpers it called: WebHttpDispatchOperationSelector, UriTemplateDispatchFormatter, WebErrorHandler, FormatSelectingMessageInspector, MultiplexingDispatchMessageFormatter, HelpPage, HttpUnhandledOperationInvoker, ...)
  • Raw octet-stream pass-through (ByteStreamMessage, MessageBodyStream, raw StreamBodyWriter)
  • JSONP (WebScriptEnablingBehavior, JavascriptCallbackBehaviorAttribute)
  • AspNetCacheProfileAttribute
  • IIS WebServiceHost / WebServiceHostFactory

Primitives changes

Just one item — minimum required for a client-only port:

  • InternalsVisibleTo("System.ServiceModel.Web") added to System.ServiceModel.Primitives.csproj so the new assembly can reach DiagnosticUtility.ExceptionUtility, Fx, XmlSerializerOperationBehavior.Reflector, internal IDispatchMessageFormatter, OperationFormatter.CreateDeserializationFailedFault, BufferedMessage/BufferedMessageData/BufferedMessageWriter, etc.

Same IVT entry added to System.ServiceModel.Http.csproj for HttpTransportDefaults.

Tests

  • Unit tests in src/System.ServiceModel.Web/tests/ — 3 tests covering binding/factory/behavior construction. All passing locally (80ms).
  • Scenario / outerloop tests in src/System.Private.ServiceModel/tests/Scenarios/Binding/WebHttp/Binding.WebHttp.IntegrationTests.csproj — 4 round-trip tests (EchoWithGet XML, EchoWithGetJson JSON, EchoWithPost, EchoWithGetPath path-var). Built and discovered; pending live CI run.
  • Server-side test host WebHttpTestServiceHost.cs in tools/IISHostedWcfService/App_code/testhosts/ with contract IWcfWebHttpService. Auto-picked-up by SelfHostedCoreWcfService via the existing <Compile Include="..\IISHostedWcfService\**\*.cs"> glob. Helix auto-discovers the new test project via the **\*.IntegrationTests.csproj glob in eng/SendToHelix.proj — no CI registration changes needed.
  • New file tests/Common/Scenarios/Endpoints.WebHttp.cs extends the existing public static partial class Endpoints with HttpBaseAddress_WebHttp[_*] properties.

Build verification

build.cmd -build -configuration Release
=> Build succeeded.
   0 Warning(s)
   0 Error(s)

Release notes

release-notes/SupportedFeatures-v2.1.0.md: WebHttpBinding bumped from 🚫 to ⚠️ (partially supported) on Windows / Linux / macOS.

Commits

  1. Scaffold System.ServiceModel.Web package (csproj, sln, IVT, EnablePackageValidation=false)
  2. Port UriTemplate cluster from .NET Framework reference source (20 files / ~4,800 LOC)
  3. Suppress SYSLIB0051 on UriTemplateMatchException serialization ctor
  4. Demote MSB3243 to message in test csproj (collision with runtime v4 shim)
  5. Port Phase 3: encoding plumbing from CoreWCF.WebHttp v1.8.0
  6. Port Phase 4: attributes, faults, web operation context
  7. Port Phase 5: WebHttpBinding, WebHttpBehavior, client-side dispatchers (the big one — ~10,300 LOC)
  8. Port WebChannelFactory + add Phase 6 test infrastructure
  9. Bump WebHttpBinding from not-supported to partially-supported

Source attribution

Every commit message documents source provenance:

  • mono/mono @ 0f53e9e1 for files lifted from the Microsoft Reference Source
  • CoreWCF/CoreWCF @ v1.8.0 for files lifted from CoreWCF.WebHttp
  • All sources MIT-licensed; .NET Foundation MIT header applied to every ported file

Copilot AI added 11 commits June 14, 2026 09:16
Add empty System.ServiceModel.Web package targeting $(DotNetVersion).
Mirrors the System.ServiceModel.NetNamedPipe template:
- Single TFM, no ref/ folder, two-entry solution wiring.
- System.SR resource class via Resources/Strings.resx auto-generation.
- ProjectReference to System.ServiceModel.Primitives and System.ServiceModel.Http.

Add InternalsVisibleTo("System.ServiceModel.Web") to
System.ServiceModel.Primitives so the upcoming WebHttp port can reach
internal helpers (Fx, DiagnosticUtility, XmlSerializerOperationBehavior.Reflector).

Disable PackageValidation for this new package (no baseline on NuGet yet).

Part of porting WebHttpBinding to .NET (Core) WCF client (issues dotnet#46, dotnet#1413).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Port 19 UriTemplate* source files (~4,800 LOC) from the MIT-licensed
Microsoft Reference Source mirror in mono/mono (commit 0f53e9e1) into
the new System.ServiceModel.Web package.

Files ported under src/System.ServiceModel.Web/src/System/:
- UriTemplate.cs              (top-level public class, template parsing + binding)
- UriTemplateTable.cs         (thread-safe template table, Match/MatchSingle)
- UriTemplateMatch.cs         (match result with BoundVariables, QueryParameters)
- UriTemplateMatchException.cs
- UriTemplateEquivalenceComparer.cs
- UriTemplateHelpers.cs       (internal escape/comparer helpers)
- UriTemplatePartType.cs      (enum)
- UriTemplate{Path,Variable,Compound,Literal}*PathSegment.cs (segment AST)
- UriTemplate{Literal,Variable}QueryValue.cs (query AST)
- UriTemplatePathPartiallyEquivalentSet.cs (internal trie helper)
- UriTemplateTableMatchCandidate.cs (internal struct)
- UriTemplateTrieNode.cs       (trie-based matching engine, ~960 LOC)
- UriTemplate{Trie,Path}*.cs   (internal trie cursors / enums)

Plus a small companion type:
- System/Collections/ObjectModel/FreezableCollection.cs (internal, ported from
  System.ServiceModel/System/Collections/ObjectModel; used by UriTemplateTable)

Transformations applied to every file:
1. MS copyright header swapped for .NET Foundation MIT header.
2. SR.GetString(...) calls rewritten to SR.Format(...) (the modern convention
   in dotnet/wcf; string.Format with a single-arg pattern is a no-op).
3. No changes to logic, type signatures, or internal layout - byte-for-byte
   equivalent to the reference source after these mechanical edits.

42 new SR strings added to Resources/Strings.resx (ObjectIsReadOnly +
41 UT* error messages); XLF localization files auto-generated by
Microsoft.DotNet.XliffTasks for all 13 target locales.

Build: 0 warnings, 0 errors.

Deps satisfied via InternalsVisibleTo from previous commit:
- DiagnosticUtility.ExceptionUtility.ThrowHelperError/Argument/ArgumentNull
- Fx.Assert

Part of porting WebHttpBinding to .NET (Core) WCF client (issues dotnet#46, dotnet#1413).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The .NET FX reference source provides the protected SerializationInfo
constructor for binary-formatter compat. On .NET 8+ this constructor is
obsolete (SYSLIB0051). Mark with [Obsolete(DiagnosticId = "SYSLIB0051")]
to pin the obsolescence inline and let it pass under -warnaserror.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Our new System.ServiceModel.Web v10 collides at reference resolution
time with the BCL shim System.ServiceModel.Web v4.0.0.0 (the one that
type-forwards only Syndication and JSON types). MSBuild emits MSB3243
("No way to resolve conflict"), arbitrarily picking our v10 - which is
the correct outcome - but the warning is escalated to an error under
-warnaserror.

Suppress the noise by adding MSB3243 to MSBuildWarningsAsMessages on
the test project only. The conflict only exists in test reference
closure (Infrastructure.Common pulls in shim transitively); the impl
project does not see it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Lifts the WebMessage / Json encoder factories and related helpers from
the MIT-licensed CoreWCF.WebHttp v1.8.0 source (CoreWCF/CoreWCF on GitHub).
CoreWCF already adapted this code from the .NET Framework reference source
to .NET Core conventions, so we avoid re-doing that work.

Files added (src/System.ServiceModel.Web/src/System/ServiceModel/Channels/):
- WebMessageEncodingBindingElement.cs - composes the WebMessageEncoder
  into a Binding. Surgically stripped IWsdlExportExtension/IWmiInstanceProvider
  and BuildChannelListener overrides (server-side only).
- WebMessageEncoderFactory.cs - factory + WebMessageEncoder that demuxes
  JSON / XML / Raw based on WebBodyFormatMessageProperty.
- JsonMessageEncoderFactory.cs - factory + JsonMessageEncoder using
  DataContractJsonSerializer.
- WebBodyFormatMessageProperty.cs - per-message format tag.
- WebContentFormat.cs + WebContentFormatHelper.cs - Json/Xml/Raw/Default enum.
- WebContentTypeMapper.cs - abstract base for Content-Type to format mapping.
- HttpStreamMessage.cs - wraps Stream as Message for raw pass-through.
- ContentEncoding.cs - charset to encoding mapping struct.
- ContentTypeHelpers.cs - new client subset of CoreWCF helper.
- JsonGlobals.cs - new minimal media-type constants.
- MessageExtensions.cs - helpers for property lookup.

Plus src/System.ServiceModel.Web/src/System/ServiceModel/Pool.cs:
- Internal non-threadsafe object pool (mirror of
  System.ServiceModel.NetFramingBase/src/.../Pool.cs).

Adaptations applied while porting CoreWCF source:
- Namespace rewrite: CoreWCF.* -> System.ServiceModel.*
- BCL types resolved via System.* namespaces.
- Access modifiers: protected override -> internal override on members
  whose Primitives base is internal (CheckEncodingVersion, IsMatch,
  IsCharSetSupported, RecycledMessageState, GetReaderAtHeader).
- Async return types: Task<Message> / Task -> ValueTask<Message> / ValueTask
  to match Primitives' MessageEncoder.ReadMessageAsync/WriteMessageAsync.
- Added sync ReadMessage(Stream,...) and WriteMessage(Message, Stream)
  overrides that wrap the async versions via GetAwaiter().GetResult().
- TraceUtility.ThrowHelperError -> DiagnosticUtility.ExceptionUtility.ThrowHelperError
  (same signature, available via InternalsVisibleTo to Primitives).
- Deleted duplicates of types already in Primitives: BufferedMessage,
  BufferedMessageData, BufferedMessageWriter, EncoderHelpers, XmlAttributeHolder,
  ReceivedMessage, BufferManagerOutputStream, StreamBodyWriter. Now resolved via
  InternalsVisibleTo (was producing CS0436 warnings).
- TransportDefaults.cs deleted (EncoderDefaults/TextEncoderDefaults/
  TransportDefaults already in Primitives).
- RawMessageEncoder getter throws NotSupportedException (raw octet-stream
  pass-through requires ByteStreamMessageEncodingBindingElement which is
  not yet in dotnet/wcf - deferred to a follow-up).

19 new SR strings added to Resources/Strings.resx covering all error paths
in the new code; 13 XLF translations auto-generated.

Build: full repo, 0 warnings, 0 errors.

Part of porting WebHttpBinding to .NET (Core) WCF client (issues dotnet#46, dotnet#1413).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Lifts the Web-namespace public types from CoreWCF.WebHttp v1.8.0:

Attributes (mark contract operations for REST dispatch):
- WebGetAttribute, WebInvokeAttribute (IOperationBehavior, no-op for client)

Enums and helpers:
- WebMessageFormat (Xml/Json), WebMessageBodyStyle (Bare/Wrapped/...)
- WebMessageFormatHelper, WebMessageBodyStyleHelper (IsDefined validators)

Exception types:
- WebFaultException, WebFaultException<T> (HTTP-status-aware faults)
- IWebFaultException (internal bridge)

OperationContext wrappers:
- WebOperationContext (IExtension<OperationContext>, Current accessor)
- IncomingWebRequestContext (~440 LOC: Headers, Method, IfMatch/None, etc.)
- OutgoingWebResponseContext (~300 LOC: SetStatusCode, SetETag, SetLastModified)
- HttpDateParse, Utility (HTTP date / ETag parsing helpers)

Adaptations applied to the CoreWCF source:
- 'using CoreWCF.Dispatcher;' -> 'using System.ServiceModel.Dispatcher;'
- 'using CoreWCF.IdentityModel.Claims;' -> 'using System.IdentityModel.Claims;'
- [Obsolete(DiagnosticId = "SYSLIB0051")] added on overrides of
  Exception.GetObjectData on WebFaultException and WebFaultException<T>.

Stubs for server-side functionality (NotSupportedException) because
dotnet/wcf is client-only:
- WebOperationContext.CreateStreamResponse(Stream|StreamBodyWriter|Action<Stream>, string)
  - raw octet-stream pass-through needs ByteStreamMessage which is not ported
- WebOperationContext.CreateTextResponse(string|Action<TextWriter>, ...)
  - depends on ActionOfStreamBodyWriter / StreamBodyWriter
- WebOperationContext.GetUriTemplate(string)
  - reaches into WebHttpDispatchOperationSelector (server-side)
- WebOperationContext.s_defaultStreamMediaType inlined to "application/octet-stream"
  (was WebHttpBehavior.s_defaultStreamContentType - WebHttpBehavior is Phase 5)
- OutgoingWebResponseContext.SuppressEntityBody no-op (the property exists on
  HttpRequestMessageProperty in dotnet/wcf but not on HttpResponseMessageProperty)
- OutgoingWebResponseContext.BindingWriteEncoding falls back to UTF-8 (the original
  walked EndpointDispatcher.Id / OperationContext.Host - both server-side only)

Added 5 more SR strings (ConditionalRetrieveGetAndHeadOnly,
ConditionalUpdatePutPostAndDeleteOnly, HttpContextNoIncomingMessageProperty,
WebHttpServerSideOperationSelectorNotSupported, WeakEntityTagsNotSupported).

Message.CreateMessage(MessageVersion.None, null, ...) calls disambiguated with
(string)null casts (the new overload set is ambiguous between string and
ActionHeader at the null literal).

Build: full repo, 0 warnings, 0 errors.

Part of porting WebHttpBinding to .NET (Core) WCF client (issues dotnet#46, dotnet#1413).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The big one. Brings the binding, behavior, factory, formatter, query
string converter and supporting helpers across from CoreWCF.WebHttp
v1.8.0 + a few small additional helpers from CoreWCF root.

Public API surface added:
- System.ServiceModel.WebHttpBinding
- System.ServiceModel.WebHttpSecurity
- System.ServiceModel.WebHttpSecurityMode (enum: None/Transport/TransportCredentialOnly)
- System.ServiceModel.Description.WebHttpBehavior (~870 LOC, IEndpointBehavior)
- System.ServiceModel.Dispatcher.QueryStringConverter
- System.ServiceModel.Dispatcher.UriTemplateClientFormatter
- System.ServiceModel.Dispatcher.SingleBodyParameter{,DataContract,XmlSerializer}MessageFormatter
- System.ServiceModel.Dispatcher.DataContractJsonSerializerOperationFormatter
- System.ServiceModel.Dispatcher.HttpStreamFormatter
- System.ServiceModel.Dispatcher.{Xml,Json,Multiplexing}FormatMapping
- System.ServiceModel.Dispatcher.UnwrappedTypesXmlSerializerManager
- System.ServiceModel.Dispatcher.NameValueCache

Internal helpers:
- System.ServiceModel.HttpTransportHelpers (auth scheme mapping)
- System.ServiceModel.DataContractHelpers
- System.ServiceModel.Description.DataContractJsonSerializerOperationBehavior
- Expanded JsonGlobals (added RootString/ItemString/TypeString/ObjectString/
  DString/NullString constants + RootDictionaryString/ItemDictionaryString/
  DDictionaryString XmlDictionaryString fields)
- IDispatchOperationSelector (internal interface; stub for client-only port)
- IDispatchFaultFormatterWrapper (internal interface; stub)

Server-side surgery applied (the client-only nature of dotnet/wcf):
- WebHttpBehavior.ApplyDispatchBehavior body replaced with
  NotSupportedException; the original wires up UriTemplateDispatchFormatter,
  WebHttpDispatchOperationSelector, FormatSelectingMessageInspector,
  WebErrorHandler, PrefixEndpointAddressMessageFilter, MatchAllMessageFilter,
  CompositeDispatchFormatter, WebFaultFormatter, HelpPage, HttpUnhandledOperationInvoker
  - none of those types are part of this port (use CoreWCF.WebHttp server-side).
- WebHttpBehavior.GetReplyDispatchFormatter / GetRequestDispatchFormatter /
  GetDefaultDispatchFormatter / GetDefaultXmlAndJsonDispatchFormatter now
  return null. Same reason - server-side dispatch.
- WebHttpBehavior.AddServerErrorHandlers no-ops (WebErrorHandler not ported).
- WebHttpBehavior.GetOperationSelector removed (WebHttpDispatchOperationSelector
  not ported).
- WebHttpBehavior.HideRequestUriTemplateParameters(... UriTemplateDispatchFormatter ...)
  removed (server-only overload).
- WebHttpBehavior method visibility: GetReplyDispatchFormatter and
  GetRequestDispatchFormatter changed from protected to internal (return type
  IDispatchMessageFormatter is internal in Primitives - server-side use only).
- SingleBodyParameterMessageFormatter.CreateXmlAndJsonDispatchFormatter
  returns the XML formatter directly (DemultiplexingDispatchMessageFormatter
  not ported); the JSON formatter is still created for diagnostics.
- SingleBodyParameterMessageFormatter.SuppressReplyEntityBody no-ops in the
  fallback raw HttpResponseMessageProperty path (SuppressEntityBody is not
  on dotnet/wcf's HttpResponseMessageProperty).
- HttpStreamFormatter.WriteObject and GetStreamFromMessage throw
  NotSupportedException (raw stream pass-through needs ByteStreamMessage /
  MessageBodyStream - deferred to a follow-up).
- WebHttpSecurity.ApplyAuthorizationPolicySupport no-ops (the
  AlwaysUseAuthorizationPolicySupport property is server-side only).
- HttpTransportHelpers.ConfigureAuthentication / DisableAuthentication no
  longer set http.Realm (Realm not exposed on dotnet/wcf's HttpTransport*).
- WebHttpBinding.WriteEncoding [TypeConverter] swapped from EncodingConverter
  (CoreWCF internal) to StringConverter.

Server-only files deleted (12) - to be reintroduced if/when dotnet/wcf grows
full dispatch surface:
  Description/WebHttpServiceModelCompat.cs,
  Dispatcher/CompositeDispatchFormatter.cs,
  Dispatcher/ContentTypeSettingDispatchMessageFormatter.cs,
  Dispatcher/DemultiplexingDispatchMessageFormatter.cs,
  Dispatcher/FormatSelectingMessageInspector.cs,
  Dispatcher/HttpUnhandledOperationInvoker.cs,
  Dispatcher/MultiplexingDispatchMessageFormatter.cs,
  Dispatcher/UriTemplateDispatchFormatter.cs,
  Dispatcher/WebErrorHandler.cs,
  Dispatcher/WebFaultFormatter.cs,
  Dispatcher/WebHttpDispatchOperationSelector.cs,
  Dispatcher/WebHttpDispatchOperationSelectorData.cs

Also bumps System.ServiceModel.Http with InternalsVisibleTo for
System.ServiceModel.Web so HttpTransportDefaults is reachable.

99 new SR strings imported from CoreWCF.WebHttp v1.8.0 Strings.resx
(skipping duplicates with existing entries from earlier phases).
13 XLF translations auto-regenerated.

Build: full repo, 0 warnings, 0 errors.

Part of porting WebHttpBinding to .NET (Core) WCF client (issues dotnet#46, dotnet#1413).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Client entry point:
- System.ServiceModel.Web.WebChannelFactory<TChannel> ported from the
  MIT-licensed mono/mono mirror of the .NET Framework Reference Source.
  CoreWCF.WebHttp does not ship this type (it is server-only), so the
  port is sourced from the same place as Phase 2 UriTemplate. Constructors
  that wrap missing ChannelFactory<T> base overloads (string
  endpointConfigurationName, Type channelType) are commented out with a
  rationale; the binding-and-URI shapes that ARE supported by Primitives'
  ChannelFactory<T> remain.
  - OnOpening auto-installs WebHttpBehavior if absent.
  - GetDefaultBinding(Uri) auto-selects WebHttpBinding (http) or
    WebHttpBinding + Transport security (https).
- WebHttpBehavior gains a parameterless public constructor that delegates
  to the (IServiceProvider) constructor with null. Required so
  WebChannelFactory<T>.OnOpening can do 'new WebHttpBehavior()'.
- WebMessageEncodingBindingElement.WriteEncoding [TypeConverter] swap
  (EncodingConverter -> StringConverter) carried over from earlier patches.

Test infrastructure:
- tools/IISHostedWcfService/App_code/testhosts/WebHttpTestServiceHost.cs
  Declares IWcfWebHttpService + WcfWebHttpService + WebHttpTestServiceHost
  (decorated with [TestServiceDefinition(BasePath = "WebHttp.svc",
  Schema = ServiceSchema.HTTP)]). CoreWCF path is no-op for behaviour wiring
  (WebHttpServiceBehavior auto-installs WebHttpBehavior when it sees a
  WebMessageEncodingBindingElement, which our WebHttpBinding produces).
  .NET FX path manually adds WebHttpBehavior in ApplyConfiguration.
- tests/Common/Scenarios/Endpoints.WebHttp.cs
  New partial extension to Endpoints with HttpBaseAddress_WebHttp[_*]
  properties. The Endpoints class is declared 'public static partial' in
  the existing Endpoints.cs so no edits to that file are required.
- tests/Scenarios/Binding/WebHttp/Binding.WebHttp.IntegrationTests.csproj
  + WebHttpBindingTests.cs (3 [OuterLoop] integration tests covering
  EchoWithGet (XML), EchoWithGetJson (JSON), EchoWithPost, EchoWithGetPath,
  plus 3 standalone unit tests). The contract IWcfWebHttpService is
  re-declared inline in the test file (mirroring the server-side host's
  copy) because IISHostedWcfService is folded into the host project via
  <Compile Include> wildcards and is not consumable as a library.

Verification:
- Full repo build: 0 warnings, 0 errors.
- 3 unit tests (WebHttpBinding_CanBeConstructed,
  WebHttpBinding_TransportMode_UsesHttps, WebChannelFactory_Endpoint_HasWebHttpBinding):
  ALL PASSED in 80ms.
- 4 outer-loop scenario tests built and discovered; pending live-service
  verification with SelfHostedCoreWcfService.

Warnings suppressed in the new test project: CS0105 (duplicate using),
CS0108 (method hide), CS0436 (SR type collision), MSB3243 (System.ServiceModel.Web
v10 collides with the runtime's v4 shim at reference-resolution time).
Same set the impl csproj already suppresses.

Part of porting WebHttpBinding to .NET (Core) WCF client (issues dotnet#46, dotnet#1413).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update release-notes/SupportedFeatures-v2.1.0.md from 🚫
to ⚠️ across Windows/Linux/macOS for WebHttpBinding now that
the client-side surface ships in System.ServiceModel.Web.dll (Phases 0-6).

The partially-supported marker reflects:
- WebHttpBinding, WebHttpSecurity, WebHttpSecurityMode: supported
- [WebGet] / [WebInvoke] attributes: supported
- UriTemplate + UriTemplateTable (path + query): supported
- WebChannelFactory<T>: supported (with the constructor overloads that
  ChannelFactory<T> exposes; (string endpointConfigurationName) and
  (Type channelType) are omitted)
- WebOperationContext + Incoming/OutgoingWebRequest/ResponseContext: supported
- WebFaultException / WebFaultException<T>: supported
- QueryStringConverter: supported
- Xml + Json client-side formatters: supported
- WebMessageEncodingBindingElement (text + JSON encoders): supported

Known gaps (deferred to follow-ups):
- Server-side dispatch (ApplyDispatchBehavior, WebHttpDispatchOperationSelector,
  WebErrorHandler, FormatSelectingMessageInspector, HelpPage): NOT supported.
  Use CoreWCF.WebHttp for the server side.
- Raw octet-stream pass-through (ByteStreamMessage, StreamBodyWriter,
  HttpStreamFormatter raw path): NOT supported (throws NotSupportedException).
- JSONP / JavaScript callback (JavascriptCallbackBehaviorAttribute,
  WebScriptEnablingBehavior): NOT supported.
- AspNetCacheProfileAttribute: NOT supported.
- IIS .NET Framework hosting (WebServiceHost, WebServiceHostFactory): NOT supported.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes CI failure on all 11 dotnet-wcf-ci legs (Windows/Linux/MacOS x
Debug/Release, with and without CoreWCF service):
  NETSDK1004: Assets file 'artifacts\\obj\\Binding.WebHttp.IntegrationTests
  \\project.assets.json' not found. Run a NuGet package restore to generate
  this file.

Root cause:
- The main build step (eng/common/cibuild.cmd) restores the project
  graph rooted at System.ServiceModel.sln.
- The subsequent Helix step runs eng/common/build.ps1 -projects
  eng/SendToHelix.proj which discovers test projects via the glob
  ..\src\System.Private.ServiceModel\tests\Scenarios\**\*.IntegrationTests.csproj
  and asks the Helix SDK to publish each one.
- Helix's per-project publish path expects each project's
  project.assets.json to already exist (it does not invoke 'dotnet restore'
  on individual projects).
- The new Binding.WebHttp.IntegrationTests.csproj was created in the
  prior commit but never registered in System.ServiceModel.sln, so the
  main build skipped its restore. All ten other Binding.*.IntegrationTests
  projects are in the sln and got restored normally.

Fix: register the project in the sln with a fresh GUID
  (F0F5C0C0-AB1D-4A86-A0A9-3D13ACB9B249), nested under the existing
  '03 - Test Projects' solution folder ({D6302510-AB10-4775-BCE9-98FA96FDEB76}).
Mirrors exactly the existing entries for Binding.Http.IntegrationTests
etc., with the standard 12 build-configuration lines.

Verification:
- Full .\\build.cmd -restore -build -configuration Release: 0 warnings, 0 errors.
- New project now appears in restore output and produces
  artifacts\\obj\\Binding.WebHttp.IntegrationTests\\project.assets.json.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CI failure analysis on PR dotnet#5959:
- All 12 'dotnet-wcf-ci' (non-corewcf) legs fail with HTML-500 responses from
  the shared bridge wcfcoresrv23.westus3.cloudapp.azure.com - an infra outage
  that affects every outerloop test (Binding.Http, Binding.WS, Client.*,
  Contract.*, Encoding.*, Extensibility.*, Security.*), NOT this PR.
- 'dotnet-wcf-with-corewcf--ci' (which uses local self-hosted CoreWCF, no
  bridge) is much cleaner - only one workitem fails: Binding.WebHttp.IntegrationTests.
  4 of 7 tests fail with:
  System.InvalidOperationException: Manual addressing is enabled on this
  factory, so all messages sent must be pre-addressed.

Root cause:
- The CoreWCF source we lifted for WebHttpBehavior.cs (Phase 5) had an empty
  ApplyClientBehavior - CoreWCF is server-only and never implemented the
  client-side wiring. As a result, no UriTemplateClientFormatter ever ran on
  outgoing messages, so the per-operation URI was never bound. The channel
  factory then tried to send each request to the endpoint base address
  (http://localhost:8081/WebHttp.svc/) with ManualAddressing = true on the
  HttpTransportBindingElement - failing fast in ApplyManualAddressing.
- The stub CoreWCF UriTemplateClientFormatter also threw
  PlatformNotSupportedException on every call - same reason.

Fix: port the real client-side wiring from the .NET Framework MIT-licensed
Reference Source mirror in mono/mono. Specifically:

1. src/.../Dispatcher/UriTemplateClientFormatter.cs:
   Replace the CoreWCF stub (DeserializeReply / SerializeRequest throw
   PlatformNotSupportedException) with the real .NET FX implementation
   (~150 LOC): binds operation parameters into the UriTemplate, sets
   Message.Headers.To from the bound URI, and applies SuppressEntityBody +
   Method on HttpRequestMessageProperty.
   Server-side WebOperationContext branch dropped; the client-only port uses
   the HttpRequestMessageProperty path unconditionally because dotnet/wcf's
   WebOperationContext does not expose OutgoingRequest (only OutgoingResponse,
   which is server-perspective).

2. src/.../Description/WebHttpBehavior.cs:
   - ApplyClientBehavior body replaced with the real .NET FX implementation:
     for each operation in the contract, build the request + reply client
     formatters, wrap in CompositeClientFormatter, set ClientOperation.Formatter,
     and add WebFaultClientMessageInspector.
   - Added the supporting client-side helper methods:
       GetRequestClientFormatter (the big one - ~80 LOC of URI-template +
         body-style routing, mirrors .NET FX)
       GetReplyClientFormatter (~30 LOC)
       GetDefaultClientFormatter (~30 LOC - harvests the WCF default formatter
         from a throwaway ClientOperation via IOperationBehavior.ApplyClientBehavior)
       GetDefaultXmlAndJsonClientFormatter (~10 LOC)
       GetDefaultContentType (~10 LOC)
       AddClientErrorInspector (~5 LOC)

3. src/.../Dispatcher/SingleBodyParameterMessageFormatter.cs:
   - Add IClientMessageFormatter interface (was IDispatchMessageFormatter only).
   - Add SerializeRequest, DeserializeReply, SuppressRequestEntityBody.
   - Add static factories CreateClientFormatter, CreateXmlAndJsonClientFormatter
     (mirror existing CreateDispatchFormatter / CreateXmlAndJsonDispatchFormatter).
   - Make nested NullMessageFormatter implement IClientMessageFormatter as well.

4. src/.../Dispatcher/HttpStreamFormatter.cs:
   - Add IClientMessageFormatter interface.
   - Add SerializeRequest (mirror of SerializeReply) and DeserializeReply.

5. New small client-side helper classes (each ~30 LOC, ported from .NET FX):
   - Dispatcher/CompositeClientFormatter.cs - request+reply pair.
   - Dispatcher/ContentTypeSettingClientMessageFormatter.cs - stamps
     outgoing Content-Type via HttpRequestMessageProperty (the .NET FX
     WebOperationContext branch is dropped for the same client-port reason).
   - Dispatcher/WebFaultClientMessageInspector.cs - surfaces HTTP 500 as
     CommunicationException so callers don't see empty payloads silently.

DemultiplexingClientMessageFormatter is deliberately NOT ported: the .NET FX
implementation switches on the inbound Content-Type to pick the XML or JSON
client formatter, but our client-side path returns the XML formatter directly
because [WebGet]/[WebInvoke].ResponseFormat already determines the wire format
at description time. Both modes (XML and JSON) round-trip through the same
SingleBodyParameter* formatter chain - the format mapping on WebHttpBinding
routing selects the encoder per-message via WebBodyFormatMessageProperty.

Verification:
- Full repo build (build.cmd -restore -build -configuration Release): 0 warnings, 0 errors.
- 3 unit tests pass locally (102ms): WebHttpBinding_CanBeConstructed,
  WebHttpBinding_TransportMode_UsesHttps, WebChannelFactory_Endpoint_HasWebHttpBinding.
- 4 outerloop tests will execute end-to-end in CI now that the formatter chain
  is wired (still requires SelfHostedCoreWcfService running locally; CI's
  'dotnet-wcf-with-corewcf--ci' leg launches it automatically).

Also rebased onto upstream/main (commit 36673ab - Skip SctRenewalRegressionTests
on CoreWCF host); no conflicts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@afifi-ins afifi-ins force-pushed the feature/webhttpbinding-porting branch from a5caed5 to 794f262 Compare June 14, 2026 06:29
afifi-ins and others added 5 commits June 14, 2026 23:38
…ameters

Without overriding BuildChannelFactory/CanBuildChannelFactory to call
InternalBuildChannelFactory, the encoder BE never got added to
context.BindingParameters. As a result, the downstream
HttpChannelFactory could not find it and silently fell back to the
default text encoder, which uses MessageVersion.Soap12 +
Addressing10 instead of MessageVersion.None.

This caused ApplyManualAddressing's 'via = toHeader' branch to be
skipped (it only fires when MessageVersion.Addressing ==
AddressingVersion.None), so even though Message.Headers.To was set
correctly by UriTemplateClientFormatter, the wire URL stayed at the
base address and CoreWCF returned its auto-generated help page,
triggering ProtocolException: content type text/html.

Fix follows the same pattern as TextMessageEncodingBindingElement,
BinaryMessageEncodingBindingElement, and MtomMessageEncodingBindingElement.

Adds WebHttpBinding_ActualWireUrl_IsBoundUriTemplate as a regression
test. It spins up a local HttpListener on 127.0.0.1:18091 and asserts
the actual GET URL is the bound URI template, not the base address.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CoreWCF.WebHttp routes the WebHttpDispatchOperationSelector's UriTemplate
table off of the URI that was passed to AddServiceEndpoint. When that URI
ends in a trailing slash (e.g. 'WebHttp.svc/'), every request below the
base path 404s because the table is anchored at the slash and nothing
matches relative paths like 'EchoWithGet?...'.

WebHttpTestServiceHost overrides Address to '' so its UriTemplates anchor
at the base path. The old format string produced 'WebHttp.svc/' for this
case, which CoreWCF rejected. When Address is empty just use the basePath
as-is. SOAP-based endpoints all use non-empty Address values, so their
formatting is unchanged.

Verified end-to-end against a local CoreWCF.WebHttp host (1.8.0):
  GET  /WebHttp.svc/EchoWithGet?message=Hello       -> 200
  GET  /WebHttp.svc/EchoWithGetPath/Hello-PATH      -> 200
  POST /WebHttp.svc/EchoWithPost                    -> 200

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
WebMessageEncoderFactory.WebMessageEncoder.IsContentTypeSupported was
chaining through RawMessageEncoder.IsContentTypeSupported(contentType)
first. The RawMessageEncoder property getter throws NotSupportedException
because ByteStreamMessageEncodingBindingElement is not yet ported, so
any content-type probe — including the application/xml replies the server
sends back for our test operations — threw before the Json/Text branches
got a chance to match.

Reorder the chain to check Json and Text first, and inline the raw
application/octet-stream check so we can answer the support question
without instantiating the (unported) raw encoder. Once Raw support is
ported we can revisit this.

Also extend the local-HttpListener regression test to assert the XML
reply round-trips back to a typed string, catching the IsContentTypeSupported
bug in addition to the wire URL bug it was already covering.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When an operation declares ResponseFormat=Json on its [WebGet]/[WebInvoke]
attribute, SingleBodyParameterMessageFormatter.CreateXmlAndJsonClientFormatter
was still returning the XML formatter, so DataContractSerializer tried to
parse the JSON response body and threw SerializationException 'Unable to
deserialize XML body with root name root' for typed string returns.

The full-fidelity solution would be DemultiplexingClientMessageFormatter
(which switches on the actual Content-Type of the incoming response) but
that class is server-side-heavy and not yet ported. As an interim, pass
the statically-declared response format from WebHttpBehavior.GetReplyClientFormatter
so JSON-typed operations select the JSON formatter at runtime.

Adds JSON-reply round-trip regression test against a local HttpListener,
alongside the existing XML round-trip test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The legacy 'dotnet-wcf-ci' pipeline runs scenario tests against the
shared bridge VM at wcfcoresrv23.westus3.cloudapp.azure.com, whose IIS
does not yet host the new WebHttp.svc endpoint this PR introduces. As a
result every WebHttp scenario test on that leg returned HTTP 500.

Add a 'Run_With_CoreWCFService' condition (mirror of the existing
Skip_CoreWCFService_FailedTest detector) and apply it to the four
WebHttp outerloop tests so they only execute on the self-hosted CoreWCF
leg (dotnet-wcf-with-corewcf--ci), where they pass end-to-end. They can
be re-enabled on the bridge once IISHostedWcfService is deployed there.

Unit tests, local-HttpListener round-trip tests, and CoreWCF outerloop
tests remain unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

Add APIs to support WebHttpBinding Add support for HTTP requests which use the GET verb

2 participants