I just got started intercepting requests to my WCF service.
I'm calling the web service with java code that looks like this ( short version )
connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Username", "Testname");
I'm receiving the request but I cant get/find the headers in the message request. I've tried something like this:
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
int headerIndex = request.Headers.FindHeader("Username", string.Empty);
var username = request.Headers["Username"]
return null;
}
But I always end up with -1 or exceptions. What is the right way to do this? Am I doing it wrong on the Java side as well?
The Headers property in the Message class will give you the SOAP headers; what you're looking for are the HTTP headers. To get to those, you should use the HttpRequestMessageProperty:
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
var prop = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
var userName = prop.Headers["Username"];
return null;
}
Related
In one of our code, we are getting below error.
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Web.Util.Misc.ThrowIfFailedHr(Int32 hresult)
at System.Web.Hosting.IIS7WorkerRequest.SetUnknownRequestHeader(String name, String value, Boolean replace)
at System.Web.HttpHeaderCollection.SetHeader(String name, String value, Boolean replace)
at System.Web.HttpHeaderCollection.Add(String name, String value)
Code is as below:
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
string correlationId = newAuditTrail.GetCorrelationIdFromRequest(request).ToString();
string url = newAuditTrail.GetUrlFromRequest(request).ToString();
HttpContext.Current.Request.Headers.Add("CorrelatinId", correlationId);
HttpContext.Current.Request.Headers.Add("Url", url);
Error is thrown on line:
HttpContext.Current.Request.Headers.Add("CorrelatinId", correlationId);
I noticed operation contract of method, it is defined as oneway.
[OperationContract(IsOneWay=true)]
If you want to add http header in the http request, please refer to the below code segements.
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
HttpRequestMessageProperty httpRequestMessage;
object httpRequestMessageObject;
if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject))
{
httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
if (string.IsNullOrEmpty(httpRequestMessage.Headers[USER_AGENT_HTTP_HEADER]))
{
httpRequestMessage.Headers[USER_AGENT_HTTP_HEADER] = this.m_userAgent;
}
}
else
{
httpRequestMessage = new HttpRequestMessageProperty();
httpRequestMessage.Headers.Add(USER_AGENT_HTTP_HEADER, this.m_userAgent);
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
}
return null;
}
We could also use WebOperationContext to add http header on the client-side.
Add Request header to WCF when using ConfigurationChannelFactory.CreateChannel
Please refer to the below discussion.
How to add a custom HTTP header to every WCF call?
Feel free to let me know if the problem still exists.
I am trying to implement a custom authentication filter in Guice. I receive the token, get the username and realm from the token and then create a Principal. Now I am stuck and I don't know how to set the Principal. It would be nice if I could just set it like this request.setUserPrincipal(principal);, but obviously I can't.
How can I do this?
My doFilter method looks like this:
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
if (authorizationHeader != null && authorizationHeader.length() > 0) {
String token = authorizationHeader.substring("Bearer".length()).trim();
if (token.length() > 0) {
try {
Credentials credentials = securityService.getCredentials(token);
String username = credentials.getUsername();
String realm = credentials.getRealm();
Principal principal = new HttpPrincipal(username, realm);
// request.setUserPrincipal(principal);
LOGGER.info(credentials);
} catch (Exception e) {
LOGGER.error(e);
}
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
The servlet spec section 13.10 says:
The container establishes the caller identity of a request prior to
dispatching the request to the servlet engine. The caller identity
remains unchanged throughout the processing of the request or until
the application sucessfully calls authenticate, login or logout on the
request.
That is the reason why there is no setUserPrincipal.
But there are good news. You can provide your own getUserPrincipal because you can provide your own HttpServletRequest object. Any servlet filter can do it. Look at your code, you are calling the chain method with two parameters: the request and the response. There is no need to pass the same objects that you receive.
The spec even provides you with a helper class: HttpServletRequestWrapper. You just create your own request class as a subclass of the wrapper and override any method that you want, like getUserPrincipal.
I have a wcf service and I am trying to call it from other client, but the response I am getting as a reply is incomplete. It stops from the
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
if (Log.IsDebugEnabled)
{
Log.Debug("VisService SOAP Response >>>");
//create a copy of the response for logging purpose
MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue);
reply = buffer.CreateMessage();
//log the copy to avoid removal of the actual response object
System.ServiceModel.Channels.Message replyCopy = buffer.CreateMessage();
MemoryStream ms = new MemoryStream();
System.Xml.XmlDictionaryWriter writer = System.Xml.XmlDictionaryWriter.CreateTextWriter(ms);
replyCopy.WriteMessage(writer);
//move the position of the cursor to the begining to
//read the entire message from start
ms.Position = 0;
string visServiceSOAPResponse = new StreamReader(ms, Encoding.UTF8).ReadLine();
Log.Debug(visServiceSOAPResponse);
//For displaying the message in the mail confirmation box
SaveResponseToLog("\nVisService SOAP Response >>>\n" + visServiceSOAPResponse);
}
}
//This function logs the SOAP
//request in the application log file
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
{
if (Log.IsDebugEnabled)
{
string visServiceSOAPRequest = request.ToString();
Log.Debug("VisService SOAP Request >>>");
Log.Debug(visServiceSOAPRequest);
//For displaying the message in the mail confirmation box
SaveResponseToLog("\nVisService SOAP Request >>>\n" + visServiceSOAPRequest);
}
return null;
}
}
and the response from the wcf is only till the "...xmlns="
Your log writing function is buggy. Please scrap it alltogether. Use AppendAllText if you want to append text to a file.
In addition, you seem to read only the first line of the response. You need to read the whole response before writing it to the file.
Edit:
Your log function has too many bugs and weird things to actually fix it. Replace it with:
private void SaveResponseToLog(string msg)
{
System.IO.File.AppendAllText(filename, msg, Encoding.UTF8);
}
I have a Custom ClientMessageInspector that records requests but not replies to my service.
The code is:
namespace MessageListener.Instrumentation
{
public class MessageInspector : IClientMessageInspector
{
private Message TraceMessage(MessageBuffer buffer)
{
// Must use a buffer rather than the original message, because the Message's body can be processed only once.
Message msg = buffer.CreateMessage();
using (RREM_GilbaneEntities3 entities3 = new RREM_GilbaneEntities3())
{
SOAPMessage soapMessages = new SOAPMessage
{
SOAPMessage1 = msg.ToString(),
created = DateTime.Now,
source = "Interface12",
sourceIP = "Interface12"
};
entities3.SOAPMessages.Add(soapMessages);
entities3.SaveChanges();
}
//Return copy of origonal message with unalterd State
return buffer.CreateMessage();
}
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
reply = TraceMessage(reply.CreateBufferedCopy(int.MaxValue));
}
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
request = TraceMessage(request.CreateBufferedCopy(int.MaxValue));
return null;
}
}
}
What seems to be happening is both AfterRecievReply and BeforeSendRequest are being called. In AfterRecieveReply before I call TraceMessage, I can see the whole reply. Inside TraceMessage, when I do:
// Must use a buffer rather than the original message, because the Message's body can be processed only once.
Message msg = buffer.CreateMessage();
it turns the reply into junk:
msg {<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header />
<soap:Body>... stream ...</soap:Body>
</soap:Envelope>}
What's going on?
The reply isn't a junk message - it's just when you call ToString on it that it doesn't show the body of the message. Remember that a message can only be consumed once; once its body is read, it cannot be read again. Since many places (including the watch window of debuggers) will call ToString on an object, this method is implemented in a way that if it doesn't know for sure that a message body can be read multiple times, then it won't, which seems to be your case. If you want to really write out the message, try using this code:
public string MessageToString(Message message) {
using (MemoryStream ms = new MemoryStream()) {
XmlWriterSettings ws = new XmlWriterSettings();
ws.Encoding = new UTF8Encoding(false);
using (XmlWriter w = XmlWriter.Create(ms)) {
message.WriteMessage(w);
w.Flush();
return ws.Encoding.GetString(ms.ToArray());
}
}
}
I am accessing a web service using WCF. Using WSHttpBinding, Security mode is set Transport (https) and client credential type is Basic. When I try to access the service using the proxy, getting an 401 unauthorized exception.
Here is the Binding
var binding = new WSHttpBinding()
{
UseDefaultWebProxy = true,
Security =
{
Mode = SecurityMode.Transport,
Transport =
{
ClientCredentialType = HttpClientCredentialType.Basic,
},
}
};
Here is the service call
var client = new InternetClient(binding, new EndpointAddress("httpsurl"));
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
client.ProcessMessage("somevalue");
When looked into Http headers using Http Analyzer
CONNECT HEADER
(Request-Line):CONNECT somehost.com:443 HTTP/1.1
Host:somehost.com
Proxy-Connection:Keep-Alive
POST HEADER
(Request-Line):POST /Company/1.0 HTTP/1.1
Content-Type:application/soap+xml; charset=utf-8
VsDebuggerCausalityData:uIDPo+voStemjalOv5LtRotFQ7UAAAAAUKLJpa755k6oRwto14BnuE2PDtYKxr9LhfqXFSOo8pEACQAA
Host:somehost.com
Content-Length:898
Expect:100-continue
Connection:Keep-Alive
If you see the header Authorization header is missing
Now my question is why WCF call missing the Authorization header? Am I missing something? . Please ask if you need more information
This is a common problem, but the situation is different from what you think.
It turns out that initially for the 1st request a WCF client that is configured to use HTTP basic authentication will nevertheless send the request without the necessary Authorization header to the server. This is the default behavior of the HttpWebRequest class used by the WCF client.
Normally, the web service server will then return a HTTP 401 Unauthorized response to the WCF client, upon which the latter will resend the message with the Authorization header. This means under normal conditions for HTTP Basic Authentication there will be a a rather useless round trip to the server.
This also explains why the header was missing in your sniffed message. Some Http sniffs possibly don't pass on the 401 response, so the whole exchange gets messed up.
The server round-trip and dependence on the 401 response can be avoided by manually injecting the required Authorization header into every request. See e.g. how to manually inject Authorization header into WCF request
As a slight modification from a previous answer, to support async / await calls, you can actually create a new OperationContext and pass it around on whatever thread you like (as long as it is not shared across concurrent threads as it isn't a thread-safe object)
var client = new MyClient();
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
var httpRequestProperty = new HttpRequestMessageProperty();
httpRequestProperty.Headers[HttpRequestHeader.Authorization] = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(client.ClientCredentials.UserName.UserName + ":" + client.ClientCredentials.UserName.Password));
var context = new OperationContext(ormClient.InnerChannel);
using (new OperationContextScope(context))
{
context.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;
return await client.SomeMethod();
}
I had the exact same issues. I was able to manually inject the authorization headers by using the following code:
var callcontext = new CAdxCallContext();
callcontext.codeLang = "ENG";
callcontext.poolAlias = "BGRTEST";
var proxy = new CAdxWebServiceXmlCCClient();
proxy.Endpoint.EndpointBehaviors.Add(new CustomEndpoint());
proxy.ClientCredentials.UserName.UserName = "USERNAME"; // Might not benecessary
proxy.ClientCredentials.UserName.Password = "PASSWORD"; // Might not benecessary
string inputXml = "<PARAM>" +
"<GRP ID= \"GRP1\">" +
"<FLD NAME = \"ITMREF\">" + "100001" + "</FLD>" +
"</GRP>" +
"</PARAM>";
CAdxResultXml response;
try
{
response = proxy.run(callcontext, "BGR_SIEPRO", inputXml);
}
catch (TimeoutException timeout)
{
Console.WriteLine(timeout.Message);
// handle the timeout exception.
proxy.Abort();
}
catch (CommunicationException commexception)
{
Console.WriteLine(commexception.Message);
// handle the communication exception.
proxy.Abort();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
proxy.Close();
}
}
public class ClientMessageInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// Nothing Here
Console.Write(reply.ToString());
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
HttpRequestMessageProperty httpRequestProperty = new HttpRequestMessageProperty();
httpRequestProperty.Headers[HttpRequestHeader.Authorization] = "Basic " +
Convert.ToBase64String(Encoding.ASCII.GetBytes("USERNAME" + ":" +
"PASSWORD"));
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestProperty);
return null;
}
}
public class CustomEndpoint : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
// Nothing here
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new ClientMessageInspector());
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
// Nothing here
}
public void Validate(ServiceEndpoint endpoint)
{
// Nothing here
}
}
Notice the Expect:100-continue in the header. That's the reason for the round trip.
Put this in your web.config and try again:
<system.net>
<settings>
<servicePointManager expect100Continue="false"/>
</settings>
</system.net>
Actually, I was wrong about this question. I did see different behaviour when running HTTP analyzer. While Http anaylzer running, my application crashed after receiving 401 response. When Http analyzer application closed, the above code worked as expected.