|
|
Browse by Tags
All Tags » HTTP » Samples (RSS)
-
To finish up the series on one-way HTTP requests , I promised to supply a custom channel that fixes the scenario of using the POX message encoder together with one-way requests. This is primarily a code post since most of the interesting discussion is already taken care of. I'm supplying a binding element, channel factory, and request channel. There isn't a channel listener or reply channel because there's no conflict on the server-side between one-way and POX. It's only the client side that requires this patch. Stick the binding element in your channel stack between the transport and the OneWay channel. CustomBinding binding = new CustomBinding( new OneWayBindingElement(), new ReplyMangler(), new TextMessageEncodingBindingElement(), new HttpTransportBindingElement() ); The channel code is mostly plumbing to make the reply run through the FilterMessage method that I posted last time. I spent less than 15 seconds testing this so it would be unwise to drop the code directly into your production system. It does appear however to do the proper job of swallowing messages that come back from the POX message encoder. class ReplyManglerChannel : ChannelBase, IRequestChannel { IRequestChannel innerChannel; public ReplyManglerChannel(ChannelManagerBase channelManager, IRequestChannel innerChannel) : base (channelManager) { this .innerChannel = innerChannel; } public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state) { return innerChannel.BeginRequest(message, timeout, callback, state); } public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state) { return BeginRequest(message, DefaultSendTimeout, callback, state); } public Message EndRequest(IAsyncResult result) { return FilterMessage(innerChannel.EndRequest(result)); } Message FilterMessage(Message reply) { if (reply == null ) { return null ; } HttpResponseMessageProperty properties = (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name]; if (properties != null && properties.StatusCode == HttpStatusCode.Accepted) { return null ; } return reply; } protected override void OnAbort() { innerChannel.Abort(); } protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state) { return innerChannel.BeginClose(timeout, callback, state); } protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) { return innerChannel.BeginOpen(timeout, callback, Read More...
|
-
The article yesterday left off by stating that while the test program works fine with the messaging defaults, things break when we switch to the POX message encoder. Today and tomorrow, we'll look at what goes wrong, why, and how to fix it. To switch to the POX message encoder, I'm going to change the MessageVersion setting of the normal text message encoder that is already in my binding. You can do this by adding the following line to the test client after creating the custom binding. binding.Elements.Find<MessageEncodingBindingElement>().MessageVersion = MessageVersion.None; Now, when I run the test program, I see the correct and expected output on the server. HTTP/1.1 POST http://localhost:8000/ ::1:1147 --> ::1:8000 [Headers] Connection: Keep-Alive Content-Length: 126 Content-Type: application/xml; charset=utf-8 Expect: 100-continue Host: localhost:8000 [Request] < Ping xmlns ="http://tempuri.org/" > < clientInfo > my info </ clientInfo > < lastSeen > 2006-08-15T00:22:36.528375-07:00 </ lastSeen > </ Ping > All of the SOAP goo is gone from the request. Compared to the request we saw yesterday, the only thing left in the request is what was formerly the contents of the body tag. However, on the client side, there's an exception being thrown after the message gets sent. Unhandled Exception: System.ServiceModel.ProtocolException: A response was received from a one-way send over the underlying IRequestChannel. Make sure the remote endpoint has a compatible binding at its endpoint (one that contains OneWayBindingElement). Server stack trace: at System.ServiceModel.Channels.RequestOneWayChannelFactory.RequestOutputChannel.ValidateResponse(Message response) at System.ServiceModel.Channels.RequestOneWayChannelFactory.RequestOutputChannel.OnSend(Message message, TimeSpan timeout) at System.ServiceModel.Channels.OutputChannel.Send(Message message, TimeSpan timeout) It's fairly easy to figure out what happened from the message and stack trace although it may not be obvious why. Inside the OneWay channel, there is validation that the system is truly acting in a one-way fashion. If you look at the code for the ValidateResponse method, you would see that it is essentially checking that the response message is null. In this case, we got a reply back from the server even though we were not expecting it to send anything back. Remember, the server knows nothing about WCF and behaves exactly the same for every request regardless Read More...
|
-
I promised yesterday that we would start using the HttpListener test program to look at some HTTP requests. I'm going to start by creating a fictional IPing service and a simple custom binding over HTTP. The test listener from yesterday was very basic so it can passively receive requests but doesn't make any meaningful replies. That means that we have to make the binding and service all use one-way calls so that they don't expect any real content to come back. [ServiceContract] public interface IPing { [OperationContract(IsOneWay = true )] void Ping( string clientInfo, DateTime lastSeen); } I'm going to magic up the client proxy for this service. This is exactly what you would get back from svcutil if you ran it against the IPing service contract. There's nothing actually exciting going on in this client class. [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute( "System.ServiceModel" , "3.0.0.0" )] public partial class PingClient : ClientBase<IPing>, IPing { public PingClient() { } public PingClient( string endpointConfigurationName) : base (endpointConfigurationName) { } public PingClient( string endpointConfigurationName, string remoteAddress) : base (endpointConfigurationName, remoteAddress) { } public PingClient( string endpointConfigurationName, EndpointAddress remoteAddress) : base (endpointConfigurationName, remoteAddress) { } public PingClient(Binding binding, EndpointAddress remoteAddress) : base (binding, remoteAddress) { } public void Ping( string clientInfo, DateTime lastSeen) { base .Channel.Ping(clientInfo, lastSeen); } } We never need to write a service implementation because we're going to hit the listener test program. Now, we can hit the "service" with an operation call to see what happens on the wire. I've got a trivial test client that creates the binding and sends a single call using the generated client. class client { static void Main(String[] args) { CustomBinding binding = new CustomBinding( new OneWayBindingElement(), new TextMessageEncodingBindingElement(), new HttpTransportBindingElement() ); PingClient client = new PingClient(binding, new EndpointAddress( "http://localhost:8000/" )); client.Open(); client.Ping( "my info" , DateTime.Now); client.Close(); } } This gives us a view of the SOAP goo and serialized service request. I didn't change the defaults for the SOAP envelope and WS addressing versions, which means that we'll have more goo than actual message. HTTP/1.1 POST http://localhost:8000/ Read More...
|
-
I think that the HttpWebRequest class for making HTTP requests in System.Net is fairly well understood but not that many people seem to make use of the HttpListener class for receiving requests. I use HttpListener when I need to see what WCF is doing under the covers when making a web request. By eliminating WCF from the server, I can play with the requests without any of the processing that might secretly go on. Today's article is an example of one of those test programs. I wrote a simple listener service that just prints out information about the request for inspection. I would use something like this for testing a WCF client that fiddles with how the request is structured. Since I don't know what the request is for, I just say that the request is accepted for future processing without giving an immediate response. This type of test program is for a one-way contract that doesn't expect any return data. using System; using System.IO; using System.Net; using System.Threading; class HttpTestListener { static bool shutdown = false ; static void Process( object state) { HttpListener listener = (HttpListener)state; listener.Start(); while (listener.IsListening) { try { HttpListenerContext context = listener.GetContext(); HttpListenerRequest request = context.Request; Console.WriteLine( "HTTP/{0} {1} {2}" , request.ProtocolVersion, request.HttpMethod, request.Url); Console.WriteLine( "{0} --> {1}" , request.RemoteEndPoint, request.LocalEndPoint); Console.WriteLine(); Console.WriteLine( "[Headers]" ); Console.WriteLine(request.Headers); if (request.HasEntityBody) { Console.WriteLine(); Console.WriteLine( "[Request]" ); StreamReader reader = new StreamReader(request.InputStream, request.ContentEncoding); Console.WriteLine(reader.ReadToEnd()); } HttpListenerResponse response = context.Response; response.StatusCode = ( int )HttpStatusCode.Accepted; response.ContentLength64 = 0; response.Close(); } catch (HttpListenerException exception) { if (!shutdown) { Console.WriteLine(exception); } } } } static void Main( string [] args) { HttpListener listener = new HttpListener(); foreach ( string arg in args) { listener.Prefixes.Add(arg); } listener.Prefixes.Add( "http://localhost:8000/" ); new Thread( new ParameterizedThreadStart(Process)).Start(listener); Console.WriteLine( "Press <ENTER> to quit." ); Console.ReadLine(); Console.WriteLine( "Shutting down..." ); shutdown = true ; listener.Close(); Thread.Sleep(1000); Environment.Exit(0); } } As you might expect, we Read More...
|
|
|
|