I'm trying to play around with WebSockets on IIS 8.5. I started off with a couple of very basic C# classes from a lesson:
using Microsoft.Web.WebSockets;
using System.Web;
public class ChatHttpHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.IsWebSocketRequest)
context.AcceptWebSocketRequest(new WebSocketChatHandler());
}
public bool IsReusable
{
get { return true; }
}
}
public class WebSocketChatHandler : WebSocketHandler
{
private static WebSocketCollection clients = new WebSocketCollection();
private string name;
public override void OnOpen()
{
this.name = this.WebSocketContext.QueryString["username"];
clients.Add(this);
clients.Broadcast(string.Format("{0} joined.", name));
}
public override void OnMessage(string message)
{
clients.Broadcast(string.Format("{0}: {1}", name, message));
}
public override void OnClose()
{
clients.Remove(this);
clients.Broadcast(string.Format("{0} left.", name));
}
}
and a simple HTML client. The project builds ok, but when I try to connect to the handler, it returns error 500. The problem is that I cannot see what the exact error is, because neither Chrome nor FF load the response body for ws:// scheme, so i cannot even see it in the Network tab of Developer Tools (though IIS provides the body, as I can see from from the response' Content-Length).
Is there a way to see the response body in this situation? Or what am I missing with WebSockets in IIS?
The problem was with web.config.
I added
<httpRuntime targetFramework="4.5.1" />
to system.web section and it finally began to work
You should be able to see the cause of the error in the Windows Event Viewer.
Fiddler will show you the connection and that it has upgraded to web socket so you can use that tool to at least show you if the connection worked or not. I'm not aware of a tool which can show you the traffic flowing over the socket once it has been upgraded although there might be one.
Better still, debug it in Visual Studio with breakpoints and 'break on exception' set. You can tell VS to use IIS as the server by right clicking the web site and going to Property Pages then Start Options. Tick Use custom server and put your URL into the textbox. Click Specific page and choose your default page.
Comparing it to my working solution using the same DLL, I don't spot any obvious issues with the handling of the socket, so I would suggest commenting out this.name = this.WebSocketContext.QueryString["username"]; for now and replacing it with this.name = "TEST"; as that appears to be about the only code which deviates from the samples. Keep it simple until its working!
Related
I spent several hours reading through many different examples and documentation for setting up a portable area with ASP.NET MVC, with the intent of sharing a common login page with authentication for multiple applications. I got it all together and it works nicely so far, but one thing I'm having trouble with is the use of message bus. I see it is a way of communicating between the Host and Portable components, but I don't see a clear way of how to do this.
For instance; if my Portable login is successful, how do I tell the Host so it can do something (set a cookie, redirect to a specific page, etc.)? Also, if I want to send something to the Portable (like the title or Assembly Version of the Host application) how would I do that? I haven't tried anything yet because I cannot seem to find a complete example.
I got it all figured out. There was a MvcContrib source code archive that I was unable to download since Google Chrome was blocking the .zip file, but I was able to get it using Internet Explorer.
Here are the important bits after adapting it to my application. Hopefully this can help someone. I did my best to format my answer properly, this is my first time posting on StackOverflow:
In the Portable class library
Create a LoginResult class using the ICommandResult interface
public class LoginResult: MvcContrib.PortableAreas.ICommandResult
{
public bool Success { get; set; }
public string Message { get; set; }
public string Username { get; set; }
}
Create a LoginMessage class that also uses the ICommandResult interface with LoginResult. The LoginMessage class has a property for a LoginViewModel I use in my Login.cshtml view (it has Username, Password, and some other additional fields I needed for the view)
public class LoginMessage : ICommandMessage<LoginResult>
{
public LoginResult Result { get; set; }
public LoginViewModel Input { get; set; }
}
In the HttpPost action of the Login controller, create an instance of LoginMessage, passing in the LoginViewModel from the login view, and send it to the Host with MvcContrib.Bus.Send
[HttpPost]
public ActionResult Login(LoginViewModel mdl)
{
// TODO: Do basic auth here first, then send to Host for additional validation
// Create and send message to the Host
var message = new LoginMessage { Input = mdl, Result = new LoginResult() };
MvcContrib.Bus.Send(message);
if (message.Result.Success)
{
// Redirect to defaultUrl set in the Host's web.config
FormsAuthentication.RedirectFromLoginPage(mdl.Username, false);
}
return View("Login", "_Layout", mdl);
}
Note: LoginMessage sets a new empty LoginResult and then waits for Success. The Success is set by the Host (shown below). I do this because certain Host applications have specific additional logic that only apply to that application, so I let the Host do what it needs and return to the Portable to let it know if it passed or failed. Eventually, I will have the basic authentication logic in the Portable first and then let the Host do the extra work, but for the sake of this example I am keeping it simple.
In the Host web application (which has a reference to my Portable dll)
Create a handler for the Portable.LoginMessage so we can read it in the Host. Note that IsValidLogin is where I will eventually do my additional authentication logic to see if the user is valid
public class LoginHandler : MvcContrib.PortableAreas.MessageHandler<Portable.LoginMessage>
{
public override void Handle(Portable.LoginMessage message)
{
if (IsValidLogin(message.Input.Username, message.Input.Password))
{
message.Result.Success = true;
message.Result.Username = message.Input.Username;
}
else
{
message.Result.Message = "Username or Password was incorrect";
}
}
private bool IsValidLogin(string username, string password)
{
// TODO: Replace with actual authentication
return username.Equals("admin") && password.Equals("password");
}
}
In the web.config, set the defaultUrl that the Portable will redirect to in the controller I described earlier, when message.Result.Success is True. You do not need to be using Forms Authentication, the mode can be set to None, but you need to have the defaultUrl for this to work.
<system.web>
<authentication mode="Forms">
<forms loginUrl="~/Portable/Login" defaultUrl="~/Home/Index" />
</authentication>
</system.web>
That's it! This was a great exercise and learning experience for me. I am still figuring out the second part of my question where I need to send information to the Portable first (like the application title and assembly version) but I'm thinking I can do pretty much the same thing but in reverse, where I send an ICommandMessage to the Portable when my Host starts up (global.asax).
This code runs every seconds for me:
public class CustomAuthorizationFilter : IDashboardAuthorizationFilter {
public bool Authorize(DashboardContext context)
{
if (HttpContext.Current.User.IsInRole("Admin"))
{
return true;
}
return false;
}
}
Could you help me why? (I managed to null check HttpContext.Current.User, but this code runs every second)
When you have the dashboard open many Ajax request are made to keep the UI up to date. These are likely the request you are seeing. You can inspect the context in the debugger to see the specific route being called. Also the F12 developer tools in chrome (network tab) will give you some insight as well.
I have a fairly simple website that I am playing with using ASP.NET Core. I am running the application from the command line and the website is returning static files but I keep getting 500 errors when I attempt to make a request that should get handled by MVC. How do I see what the error is? Whether the error is displayed to the browser or logged to the console doesn't matter I just want a way to see what the error is.
Add the error page middleware as shown here:
app.UseDeveloperExceptionPage();
Update for beta8:
In beta8 Microsoft changed the name to UseDeveloperExceptionPage. So if you want to use the ErrorPage, call:
app.UseDeveloperExceptionPage();
Here is the link to the related Github issue.
The ErrorPageOptions are the same as in beta6/7.
You can use
app.UseErrorPage(ErrorPageOptions.ShowAll)
until beta5 of Asp.Net Mvc.
As of beta6, ErrorPageOptions.ShowAll has been removed. You can now use the version without parameters
app.UseErrorPage();
or create an ErrorPageOptions object and specify how many lines around the error you want to display by setting SourceCodeLineCount.
app.UseErrorPage(new ErrorPageOptions() {SourceCodeLineCount = 100});
Additional Information
They removed multiple properties of ErrorPageOptions in this commit.
Before:
public class ErrorPageOptions
{
private bool _defaultVisibility;
private bool? _showExceptionDetails;
private bool? _showSourceCode;
private bool? _showQuery;
private bool? _showCookies;
private bool? _showHeaders;
private bool? _showEnvironment;
...
}
After:
public class ErrorPageOptions
{
public int SourceCodeLineCount { get; set; }
public IFileProvider FileProvider { get; set; }
...
}
So now you can only set how many lines of source code are printed.
If you don't care that your error details would be exposed to the world, you can enable the error details, right in the browser without any code changes. (This was only tested in IIS 8.5):
In IIS Manager, in the left Connections section, left-click select your Site.
In the right side Feature View open Error Pages.
On the far right Actions section, click on Edit Feature Settings
In the Error Responses, select the 2nd, Detailed errors, option then Ok (or if you are worried about exposing stuff to the world, start with the 3rd option, if you can open a local browser... ie, localhost:...)
This should be enough for you to be able to see the exact error... Important: If you had to use the middle Detailed errors option, be sure to turn it off once you debug the problem. This can give a hacker all he needs to break into your server.
If it is not important to expose the detail of the error to the world, then you can activate detailed error page in web.config.
Just add <customErrors mode="Off"/> in the <configuration> / <system.web> of your web.config file located in root folder of your web site.
For more detailed explanation:
How to Use Web.Config customErrors for ASP.NET
This has the advantage that you don't have to redeploy your site
My infrastructure:
Main - ServiceStack self hosted console app. 'Main' sends messages to MQ.
Background - ServiceStack self hosted console app. 'Background' receives messages from MQ.
Locally installed Redis
In 'Main' AppHost I configure Redis manager:
container.Register<IRedisClientsManager>(
new PooledRedisClientManager("localhost:6379"));
Then I run this code somewhere in service:
using (var client = new RedisMessageQueueClient(TryResolve<IRedisClientsManager>()))
{
client.Publish(new TestMessage { Value = "From ping" });
}
Everything works great and I can get message in my 'Background'. But problem comes when I wrap this code in class:
public class MessageQueuePublisher : IMessageQueuePublisher
{
public void Publish(object message)
{
using (var client = new RedisMessageQueueClient(
EndpointHost.AppHost.TryResolve<IRedisClientsManager>()))
{
client.Publish(message);
}
}
}
When I call MessageQueuePublisher.Publish method from the exactly same place where previous code was executed, it seems like it works correctly (no exceptions are thrown), but my message doesn't reach 'Background'.
Is this OK?
I found a solution. On my 'Background' I expect message with type TestMessage
mqService.RegisterHandler<TestMessage>(ServiceController.ExecuteMessage);
But when using MessageQueuePublisher.Publish message was of type object and went to the object queue and wasn't handled.
So to solve this problem Publish method should be generic:
public void Publish<T>(T message)
It doesn't change how method is called but code is not so good because if you look at it, it's not clear why generic is used. But at least it works.
long time ago I wrote webservice that is still in use. Now I plan to refactor it. The webservice is full of most likely unused functions and I have no idea how it is used by the clients. In order to strip away the unused functions I need to analyze the function calls and data of currently installed webservice.
Is there a (free/opensource) tool that will enable me to log all activities of the webservice.
The ideal output of the tool I'm looking for could be a database containing all the called functions and a list of the data that was send to it for each call.
Solution
With the help of Martins answer I created this HttpModule which does exactly what I wanted:
public class LoggingModule : IHttpModule
{
void IHttpModule.Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(BeginRequest);
}
private void BeginRequest(object sender, EventArgs e)
{
TryAppendLog("Content-Type");
TryAppendLog("SOAPAction");
}
void TryAppendLog(string key)
{
string value = HttpContext.Current.Request.Headers[key];
if (string.IsNullOrEmpty(value)) { return; }
HttpContext.Current.Response
.AppendToLog(string.Format("{0}: {1} ", key, value));
}
#region IHttpModule Member
public void Dispose() { }
#endregion
}
As Kobi wrote, you can find the required information in the IIS log files (i.e. in c:\WINDOWS\system32\LogFiles\W3SVC1).
If you want to log the usage into a database, you could write a simple HttpModule, which checks every request, and logs it into the DB if it is a call to your web service.
E.g. here's the relevant parts of a very simple HttpModule, which logs calls to mywebservice.asmx:
public class MyWebServiceDiagnosticsModule : IHttpModule
{
public MyWebServiceDiagnosticsModule ()
{
}
void IHttpModule.Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(BeginRequest);
}
private void BeginRequest(object sender, EventArgs e)
{
HttpContext ctx = HttpContext.Current;
string url = ctx.Request.Url.ToString().ToLower();
if (url.Contains("mywebservice.asmx"))
{
LogMethodCall(url); // parse URL and write to DB
}
}
}
You can potentially write your own IHttpHandler that would log all the information and then delegate the call to appropriate .NET HTTP Handler, but that wouldn't be a simple task.
And a word on terminology. "Refactoring" is not about changing external behavior, so if refactoring is really what you're heading for, I'd recommend to keep the public contract (interface) of the web service intact. Instead, roll out a new version of the same service with only core functionality.
You can enable logging in the IIS, they can get very detailed depending on your choices.
There are tools made specifically for analyzing IIS logs.
Depending a little bit on your load/criticality and similar constraints you could also probably just route the traffic through as Soap Proxy like SoapUI to capture and analyze traffic for a period of time. If you set up the proxy and re-route at the firewall level it should be transparent for end-users.
I have not tried this for a system with heavy load; be warned.