HttpModule isn't called on every request - c#

I have an HTTP Module that I installed on our server. What's weird is that it works but every once in awhile it's not being executed. I have logging and during the times that it doesn't work it doesn't reach the code that logs. I don't see anything in the IIS logs or the event viewer either.
namespace RedirectModule
{
public class RedirectModule : System.Web.IHttpModule
{
private const string MobileUserAgent = "MobileUserAgentCacheKey";
private const string
STRING_TO_FIND = "info/lps";
private const string STRING_TO_ADD = "/mobile";
public void Dispose()
{
//clean-up code here.
}
public void Init(HttpApplication context)
{
context.BeginRequest += context_BeginRequest;
}
private static object sync = new object();
private void context_BeginRequest(object sender, EventArgs e)
{
try
{
HttpContext context = HttpContext.Current;
string url = context.Request.Url.ToString().ToLower();
if (!url.Contains(STRING_TO_FIND) || url.Contains(STRING_TO_FIND + STRING_TO_ADD))
return;
Logger.Current.Log("Starting Redirect Phase");
if (XmlToValues.IsMobile(context.Request.ServerVariables["HTTP_USER_AGENT"],
GetCachedFile(context, "Settings.xml")))
{
var mobileRedirect = GetRedirectUrl(url, STRING_TO_FIND, STRING_TO_ADD);
if (mobileRedirect != null)
{
Logger.Current.Log("Redirect to Mobile page");
context.Response.Redirect(mobileRedirect);
}
}
Logger.Current.Log("Web Page");
Logger.Current.Log("End Begin Request");
}
catch (Exception ex)
{
if (ex is ThreadAbortException)
return;
Logger.Current.LogError(ex);
}
}
public static string GetRedirectUrl(string url, string strToFind, string strToAdd)
{
try
{
Logger.Current.Log("Get Redirect Url ");
int idx = url.IndexOf(strToFind) + strToFind.Length;
return url.Substring(0, idx) + strToAdd + url.Substring(idx);
}
catch (Exception ex)
{
Logger.Current.LogError(ex);
return null;
}
}
private XmlNodeList GetCachedFile(HttpContext context, string filePath)
{
try
{
Logger.Current.Log("GetCachedFile START");
if (context.Cache[MobileUserAgent] == null)
{
context.Cache[MobileUserAgent] = XmlToValues.GetMobileUserAgents(filePath);
Logger.Current.Log("Add Mobile File to Cache");
}
return (XmlNodeList)context.Cache[MobileUserAgent];
}
catch (Exception ex)
{
Logger.Current.LogError(ex);
return null;
}
}
}
}
and my Web.Config:
<?xml version="1.0" encoding="UTF-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="RedirectModule" />
<add name="RedirectModule" type="RedirectModule.RedirectModule, RedirectModule" />
</modules>
<handlers>
<remove name="Redirect" />
</handlers>
<validation validateIntegratedModeConfiguration="false"/>
</system.webServer>
<system.web>
<httpModules>
<add name="RedirectModule" type="RedirectModule.RedirectModule, RedirectModule" />
</httpModules>
<compilation debug="true">
</compilation>
</system.web>
</configuration>
p.s. I took out the log4net in the web.config as it's cumbersome.
Here's the link to the project: http://www.sendspace.com/file/w42me5
This is the markup of the page being requested, it's in a file called index.htmnl:
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<!-- no cache headers -->
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="no-cache">
<meta http-equiv="Expires" content="-1">
<meta http-equiv="Cache-Control" content="no-cache">
<!-- end no cache headers -->
</head>
<body>
MOBILE
</body>
</html>

I had a similar prblem... Try to turn of caching in your webbrowser and try again. In order to turn off caching for this requests youll need to modify response header. Example on modifying caching option

It sounds like you're probably hitting a cache somewhere between the browser and the server. There's a lot of potential places for the cache to be, here's some general steps to try and find it:
Browser - The browser does not have to request what it has cached. This question lists tools for different browsers to confirm what requests are being sent.
Client Computer - If a request is being sent from the browser, you may have a proxy (like Squid) returning cached data. You can use Fiddler to check if a HTTP request ever leaves the client computer.
Network Proxy - Same idea as the client computer, just the proxy is located on a server somewhere on your network. You would have to analyze outgoing HTTP traffic at the proxy server to determine if a request is being sent or not.
Web Server - Your redirect module can serve cached content instead of rendering it new. In this case, it should show up in your logs. Add a log call to the very beginning of your BeginRequest handler to confirm if the request actually makes it to your server.
To prevent caching, you can add add no-cache headers to the request (article here):
private void Application_EndRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.ExpiresAbsolute = DateTime.Now.AddDays( -100 );
context.Response.AddHeader( “pragma”, “no-cache” );
context.Response.AddHeader( “cache-control”, “private” );
context.Response.CacheControl = “no-cache”;
}
EDIT
Debugging what request get to the server
HTTP 200 in the response suggests that it's very likely you do not have a cache issue. To confirm every request is actually getting to the server, try adding a log to your Application_BeginRequest handler (in Global.asax) to log every request and compare it to the log generated by your context_BeginRequest in your HttpModule.
//In Global.asax
private void Application_BeginRequest(Object source, EventArgs e) {
Logger.Current.Log("Request made to Application_BeginRequest")
}
//In your HttpModule
private void context_BeginRequest(object sender, EventArgs e)
{
//Log before any of your other code
Logger.Current.Log("Request made to context_BeginRequest")
try
{
// your code
}
catch (Exception ex)
{
// log the exception first in case this is the problem
Logger.Current.LogError(ex);
if (ex is ThreadAbortException)
return;
}
}
Interpreting the results
Check your log after a request that is not getting to your module.
If it shows nothing, the request never made it to your server. See above about caches
If your log end at Request made to Application_BeginRequest, your module is not being called.
If it ends at Request made to context_BeginRequest, there's an error in the module code before your logger which is causing a crash on some requests. This could be a null reference HttpContext.Current, or Request, or a problem with overflows, or any number of other things. This should be logged by your finally statement.

Related

How can I force the login screen to appear when user re-opens their web browser after auto-logged out time?

Good afternoon.
I have a situation with a SSL web application written in C# in that if I log in and navigate to a secure page, close my browser and open it again after the login has expired, the content that was shown when closing the browser still appears.
In other words, if the login cookie expires after 30 minutes, and I close the browser and re-open it (restoring my tabs) after 30 minutes, the page that I was viewing still appears and not the expected login page.
If I click on a link after re-opening the browser that is supposed to take me to another secure location, I am redirected to the login page, but I'd like to prevent the previous page content from being shown again since it could contain sensitive data.
The page content is set to expire after 30 minutes and the user login cookie is set to expire after 30 minutes as well. (See images below)
I'm not sure how to force the login screen to appear when re-opening a browser. Thoughts or help would be greatly appreciated!
EDIT: Relevant content of web.config if it will help.:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
[REMOVED]
<authentication mode="Forms">
<forms path="/" cookieless="UseCookies" loginUrl="~/Account/Login.aspx" name="[REMOVED]" requireSSL="true" timeout="30" />
</authentication>
[REMOVED]
</system.web>
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="00:30:00" />
</staticContent>
[REMOVED]
</system.webServer>
[REMOVED]
</configuration>
OnLoad Method from the Site.Master page:
protected void Page_Load(object sender, EventArgs e)
{
/*
* See if the user has read the service agreement, if not, send the user to the service agreement page.
* Do NOT process this if it is being called from the service agreement page!
* If you do, we will end up with an infinite loop and infinite loops are bad mmmkay?
*/
if (!Profile.IsAnonymous && !Roles.IsUserInRole("Administrator"))
{
if (!Profile.ServiceAgreementStatusOkay())
{
if (Page.Request.RawUrl != #"/Account/KB/SA/")
{
Response.Redirect("/Account/KB/SA/");
}
}
}
// only check messages if the user isn't anonymous
if (!Profile.IsAnonymous)
{
int UnreadMessageCount = 0;
SqlDataSourceMessageCount.SelectParameters["UserId"].DefaultValue = Membership.GetUser().ProviderUserKey.ToString();
SqlDataSourceMessageList.SelectParameters["UserId"].DefaultValue = Membership.GetUser().ProviderUserKey.ToString();
Repeater MessageListRepeater = ((Repeater)LoginViewUserMenu.FindControl("RepeaterMessages"));
SqlDataReader CountReader = null;
DataView MessageListDataView = new DataView();
try
{
CountReader = (SqlDataReader)SqlDataSourceMessageCount.Select(DataSourceSelectArguments.Empty);
if (CountReader.HasRows)
{
CountReader.Read();
UnreadMessageCount = CountReader.SafeGetInt32(0);
if (UnreadMessageCount != 0)
{
((Literal)LoginViewUserMenu.FindControl("LiteralMessageCount")).Text = UnreadMessageCount.ToString();
}
else
{
((Literal)LoginViewUserMenu.FindControl("LiteralMessageCount")).Text = string.Empty;
}
}
SqlDataSourceMessageList.FilterParameters.Clear();
SqlDataSourceMessageList.FilterExpression = string.Empty;
SqlDataSourceMessageList.FilterParameters.Add("IsRead", "false");
SqlDataSourceMessageList.FilterExpression = "IsRead = {0}";
MessageListDataView = (DataView)SqlDataSourceMessageList.Select(DataSourceSelectArguments.Empty);
MessageListRepeater.DataSource = MessageListDataView;
MessageListRepeater.DataBind();
}
catch (Exception ex)
{
ErrorNotifier Err = new ErrorNotifier();
Err.Notify(ex, HttpContext.Current.Request.Url.AbsoluteUri.ToString());
throw;
}
finally
{
if (CountReader != null)
{
if (!CountReader.IsClosed)
{
CountReader.Close();
}
}
}
}
// set the value of the bug report path.
TextBoxPath.Text = Page.Request.RawUrl;
// set footer stuff.
AppCopyrightDate.Text = AppAttributes.GetCopyrightString();
LiteralVersionNumber.Text = AppAttributes.AppVersion.ToString();
// set theme info.
ThemeManager TM = new ThemeManager();
if (Request["ThemePreview"] == null)
{
LiteralTheme.Text = TM.GetThemeLink(Profile.UX.Theme);
}
else
{
LiteralTheme.Text = TM.GetThemeLink(Request["ThemePreview"]);
}
}

Custom Error Page is not working on live server

I have implemented Custom Error Functionality in my project, and its working on local IIS but not working on live server. I have implemented this functionality using Global.asax file and i am calling my custom error action method in my custom error controller in MVC. I have published and run on local IIS and its work well,but on live server.
my Global.asax.cs file
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//do not register HandleErrorAttribute. use classic error handling mode
filters.Add(new HandleErrorAttribute());
}
protected void Application_Error(Object sender, EventArgs e)
{
LogException(Server.GetLastError());
CustomErrorsSection customErrorsSection = (CustomErrorsSection)ConfigurationManager.GetSection("system.web/customErrors");
string defaultRedirect = customErrorsSection.DefaultRedirect;
if (customErrorsSection.Mod e== CustomErrorsMode.On)
{
var ex = Server.GetLastError().GetBaseException();
Server.ClearError();
var routeData = new RouteData();
routeData.Values.Add("controller", "Common");
routeData.Values.Add("action", "CustomError");
if (ex is HttpException)
{
var httpException = (HttpException)ex;
var code = httpException.GetHttpCode();
routeData.Values.Add("status", code);
}
else
{
routeData.Values.Add("status", 500);
}
routeData.Values.Add("error", ex);
IController errorController = new Test.Controllers.CommonController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
}
My Custom error Controller and its action method
public ActionResult CustomError(int status, Exception error)
{
var model = new CustomErrorModel();
model.Code = status;
model.Description = Convert.ToString(error);
Response.StatusCode = status;
return View(model);
}
So what should I do?
I had this problem where errors on live IIS server weren't showing custom error pages (which return proper HttpStatusCodes) but it WAS working on local IIS (localhost address using default website - not Cassini). They should have worked exactly the same I would have thought - anyway this web.config setting fixed it.
<configuration>
<system.webServer>
<httpErrors existingResponse="PassThrough"></httpErrors>
</system.webServer>
</configuration>
Note that my setup uses Application_Error in global.asax and just this other web.config setting:
<customErrors mode="On">
<!-- There is custom handling of errors in Global.asax -->
</customErrors>
2 approaches
Route Method
// We couldn't find a route to handle the request. Show the 404 page.
routes.MapRoute("Error", "{*url}", new { controller = "Error", action = "CustomError" } );
or
Custom Error Handler in Web.config:
<customErrors mode="On" >
<error statusCode="404" redirect="~/CatchallController/CustomError" />
</customErrors>
The condition raised by no route matching is a 404. This way you direct all non-match to your ~/CatchallController/CustomError

DNN 6 Module - How to leverage asynchronous calls

DotNetNuke 6 does not appear to support WebMethods due to modules being developed as user controls, not aspx pages.
What is the recommended way to route, call and return JSON from a DNN user module to a page containing that module?
It appears the best way to handle this problem is custom Httphandlers. I used the example found in Chris Hammonds Article for a baseline.
The general idea is that you need to create a custom HTTP handler:
<system.webServer>
<handlers>
<add name="DnnWebServicesGetHandler" verb="*" path="svc/*" type="Your.Namespace.Handler, YourAssembly" preCondition="integratedMode" />
</handlers>
</system.webServer>
You also need the legacy handler configuration:
<system.web>
<httpHandlers>
<add verb="*" path="svc/*" type="Your.Namespace.Handler, YourAssembly" />
</httpHandlers>
</system.web>
The handler itself is very simple. You use the request url and parameters to infer the necessary logic. In this case I used Json.Net to return JSON data to the client.
public class Handler: IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
//because we're coming into a URL that isn't being handled by DNN we need to figure out the PortalId
SetPortalId(context.Request);
HttpResponse response = context.Response;
response.ContentType = "application/json";
string localPath = context.Request.Url.LocalPath;
if (localPath.Contains("/svc/time"))
{
response.Write(JsonConvert.SerializeObject(DateTime.Now));
}
}
public bool IsReusable
{
get { return true; }
}
///<summary>
/// Set the portalid, taking the current request and locating which portal is being called based on this request.
/// </summary>
/// <param name="request">request</param>
private void SetPortalId(HttpRequest request)
{
string domainName = DotNetNuke.Common.Globals.GetDomainName(request, true);
string portalAlias = domainName.Substring(0, domainName.IndexOf("/svc"));
PortalAliasInfo pai = PortalSettings.GetPortalAliasInfo(portalAlias);
if (pai != null)
{
PortalId = pai.PortalID;
}
}
protected int PortalId { get; set; }
}
A call to http://mydnnsite/svc/time is properly handled and returns JSON containing the current time.
does anyone else have an issue of accessing session state/updating user information via this module? I got the request/response to work, and i can access DNN interface, however, when i try to get the current user, it returns null; thus making it impossible to verify access roles.
//Always returns an element with null parameters; not giving current user
var currentUser = UserController.Instance.GetCurrentUserInfo();

Handling Endpoint exceptions in WCF

in my application (Windows Phone), I generate a connection to a WCF web service dynamically via code behind.
The user must specify the url of the Web Service then I create my EndPointAddress and Binding, and it works well.
But if the user enter a invalid url, an exception is thrown :
"System.ServiceModel.EndpointNotFoundException -> There Was no endpoint listening at [address of the service] That Could accept the message. This Is Often Caused By An incorrect address or SOAP action. See InnerException, if present, for more details."
The innerexception is quite classic : "The remote server Returned an error: NotFound at InnerException."
The problem: I can not handle this exception. I tried many things found here without success.
Is there any solution?
The only thing I could find is surronding some methods in the Reference.cs, which is definitly not a good idea ...!
Thank you in advance and sorry for my bad english :) !
My actual code looks like this, but it doesn't catch the exception =>
MyServiceClient client = new MyServiceClient(myBinding, myEndpointAddress);
try
{
client.RegisterUserCompleted += new EventHandler<RegisterUserCompletedEventArgs>(client_RegisterUserCompleted);
client.RegisterUserAsync();
}
catch (TimeoutException ex)
{
MessageBox.Show(ex.Message);
client.Abort();
}
catch (FaultException ex)
{
MessageBox.Show(ex.Message);
client.Abort();
}
catch (CommunicationException ex)
{
MessageBox.Show(ex.Message);
client.Abort();
}
catch
{
MessageBox.Show("Error");
client.Abort();
}
finally
{
client.CloseAsync();
}
the problem is solved.
The solution is : the uri have to be test before creating the client.
To do it, I make a WebRequest and catch the exception in the WebResponse :
public void ValidateUri(String uri)
{
if (!Uri.IsWellFormedUriString(uri, UriKind.RelativeOrAbsolute)) return;
request = WebRequest.Create(uri);
request.BeginGetResponse(new AsyncCallback(ValidateUriCallback), null);
}
private WebRequest request;
private void ValidateUriCallback(IAsyncResult result)
{
try
{
WebResponse httpResponse = (WebResponse)request.EndGetResponse(result);
// Create the client here
ServiceClient client = new MyServiceClient(myBinding, myEndpointAddress);
client.RegisterUserCompleted += new EventHandler<RegisterUserCompletedEventArgs>(client_RegisterUserCompleted);
client.RegisterUserAsync();
}
catch (WebException ex)
{
var response = ex.Response as System.Net.HttpWebResponse;
if (response!=null
&& response.StatusCode == HttpStatusCode.NotFound)
{
Dispatcher.BeginInvoke(delegate()
{
MessageBox.Show("The web service address is not valid", "Sorry", MessageBoxButton.OK);
});
}
}
You need move your try catches to the client_RegisterUserCompleted method and wrap around the var x = e.Result;
EDIT: Since my first approach was wrong...
First you should start by inspecting your URL (myEndpointAddress in your code). Can you browse to it in a browser (you can set a breakpoint on that line, then copy the value and paste it into your browser).
If that works or if there is an error shown when you browse to it, then add this to your web.config of the web app hosting the WCF service inside of the
<system.diagnostics> <sources> <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true"> <listeners> <add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData= "Trace.svclog" /> </listeners> </source> </sources> </system.diagnostics>
Then run your application or browse to it. It will create a Trace.svclog file in the same directory as your web application. Open up that file and look for the highlighted in red entry. That will give you more information on the exception. WCF doesn't return the full information by default to slow down malicious users.

Uploadify ashx file Context.Session gets null

I have a file upload in my site which is done using uploadify it uses a ashx page to upload file to database.It works fine in IE but in Mozilla the context.Session is getting null.I have also used IReadOnlySessionState to read session.
how can i get session in Mozilla like IE.
here is the ashx code i have done
public class Upload : IHttpHandler, IReadOnlySessionState
{
HttpContext context;
public void ProcessRequest(HttpContext context)
{
string UserID = context.Request["UserID"];
context.Response.ContentType = "text/plain";
context.Response.Expires = -1;
XmlDocument xDoc = new XmlDocument();
HttpPostedFile postedFile = context.Request.Files["Filedata"];
try
{
if (context.Session["User"] == null || context.Session["User"].ToString() == "")
{
context.Response.Write("SessionExpired");
context.Response.StatusCode = 200;
}
else
{
// does the uploading to database
}
}
}
}
In IE Context.Session["User"] always have the value but in Mozilla it is always null
You need to add sessionId to uploadify post params and restore ASP.NET_SessionId cookie on the server side on global.asax at OnBeginRequest. It is actually bug with flash and cookies.
I have created module for session and auth cookie restore, to get work flash and asp.net session, so i think it will be useful for your:
public class SwfUploadSupportModule : IHttpModule
{
public void Dispose()
{
// clean-up code here.
}
public void Init(HttpApplication application)
{
application.BeginRequest += new EventHandler(OnBeginRequest);
}
private void OnBeginRequest(object sender, EventArgs e)
{
var httpApplication = (HttpApplication)sender;
/* we guess at this point session is not already retrieved by application so we recreate cookie with the session id... */
try
{
string session_param_name = "ASPSESSID";
string session_cookie_name = "ASP.NET_SessionId";
if (httpApplication.Request.Form[session_param_name] != null)
{
UpdateCookie(httpApplication, session_cookie_name, httpApplication.Request.Form[session_param_name]);
}
else if (httpApplication.Request.QueryString[session_param_name] != null)
{
UpdateCookie(httpApplication, session_cookie_name, httpApplication.Request.QueryString[session_param_name]);
}
}
catch
{
}
try
{
string auth_param_name = "AUTHID";
string auth_cookie_name = FormsAuthentication.FormsCookieName;
if (httpApplication.Request.Form[auth_param_name] != null)
{
UpdateCookie(httpApplication, auth_cookie_name, httpApplication.Request.Form[auth_param_name]);
}
else if (httpApplication.Request.QueryString[auth_param_name] != null)
{
UpdateCookie(httpApplication, auth_cookie_name, httpApplication.Request.QueryString[auth_param_name]);
}
}
catch
{
}
}
private void UpdateCookie(HttpApplication application, string cookie_name, string cookie_value)
{
var httpApplication = (HttpApplication)application;
HttpCookie cookie = httpApplication.Request.Cookies.Get(cookie_name);
if (null == cookie)
{
cookie = new HttpCookie(cookie_name);
}
cookie.Value = cookie_value;
httpApplication.Request.Cookies.Set(cookie);
}
}
Also than you need register above module at web.config:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="SwfUploadSupportModule" type="namespace.SwfUploadSupportModule, application name" />
</modules>
</system.webServer>
Context.Session is null.. because connection to HttpHandler has another Context.Session
(debug and try: Context.Session.SessionId in where is the fileInput is different from Context.Session.SessionId in Upload.ashx)!
I suggest a workaround: pass a reference to the elements you need in the second session ( in my sample i pass the original SessionId using sessionId variable)
....
var sessionId = "<%=Context.Session.SessionID%>";
var theString = "other param,if needed";
$(document).ready(function () {
$('#fileInput').uploadify({
'uploader': '<%=ResolveUrl("~/uploadify/uploadify.swf")%>',
'script': '<%=ResolveUrl("~/Upload.ashx")%>',
'scriptData': { 'sessionId': sessionId, 'foo': theString },
'cancelImg': '<%=ResolveUrl("~/uploadify/cancel.png")%>',
....
and use this items in .ashx file.
public void ProcessRequest(HttpContext context)
{
try
{
HttpPostedFile file = context.Request.Files["Filedata"];
string sessionId = context.Request["sessionId"].ToString();
....
If you need to share complex elements use Context.Application instead of Context.Session, using original SessionID: Context.Application["SharedElement"+SessionID]
It's likely to be something failing to be set by the server or sent back on the client.
Step back to a lower level - use a network diagnostic tool such as Fiddler or Wireshark to examine the traffic being sent to/from your server and compare the differences between IE and Firefox.
Look at the headers to ensure that cookies and form values are being sent back to the server as expected.
I have created a function to check session have expired and then pass that as a parameter in script-data of uploadify and in ashx file i check that parameter to see whether session exists or not.if it returns session have expired then upload will not take place.It worked for me. Did not find any issues using that. hope that solve my issue
I had a similar problem with an .ashx file. The solution was that the handler has to implement IReadOnlySessionState (for read-only access) or IRequiresSessionState (for read-write access). eg:
public class SwfUploadSupportModule : IHttpHandler, IRequiresSessionState { ... }
These Interfaces do not need any additional code but act as markers for the framework.
Hope that this helps.
Jonathan

Categories