I have an HttpHandler mapped to aspnet_isapi.dll to perform a custom authentication check on static files (.pdf files) using IIS 7.5 in Classic mode:
void IHttpHandler.ProcessRequest(HttpContext context)
{
if(!User.IsMember) {
Response.Redirect("~/Login.aspx?m=1");
}
else {
//serve static content
}
}
The above code works fine, except for the else statement logic. In the else statement, I simply want to allow the StaticFileHandler to process the request, but I haven't been able to sort this out. Any suggestions on how to simply "hand off" the file back to IIS to serve the request as a normal StaticFile request, would be appreciated.
To answer your question directly, you can create a StaticFileHandler and have it process the request:
// Serve static content:
Type type = typeof(HttpApplication).Assembly.GetType("System.Web.StaticFileHandler", true);
IHttpHandler handler = (IHttpHandler)Activator.CreateInstance(type, true);
handler.ProcessRequest(context);
But a better idea might be to create an HTTP module instead of an HTTP handler:
public class AuthenticationModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication application)
{
application.AuthorizeRequest += this.Application_AuthorizeRequest;
}
private void Application_AuthorizeRequest(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
if (!User.IsMember)
context.Response.Redirect("~/Login.aspx?m=1");
}
}
Related
I have this module:
public class SecureCookieModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.EndRequest += OnEndRequestHandlerExecute;
}
private void OnEndRequestHandlerExecute(object sender, EventArgs e)
{
if (!AppConfig.MachineAppSettingsBool("setSecureCookiesForSecureConnections"))
{
return;
}
try
{
HttpApplication app = sender as HttpApplication;
if (app.Request.IsReallySecureConnection())
{
foreach (string s in app.Response.Cookies.AllKeys)
{
var cookie = app.Response.Cookies[s];
if (cookie.Secure == false)
{
cookie.Secure = true;
app.Response.Cookies.Add(cookie);
}
}
}
}
catch (Exception ex)
{
LogManager.GetLogger("SecureCookieModule").Error("Exception while processing http module. Ex: {0}", ex);
}
}
}
From this what I wrote in some cases the response headers are already sent, so they cannot be modified and exception is thrown because of this. What I miss is in which cases this happens and why? Why in some cases before executing the "OnEndRequestHandlerExecute" event they are sent and in other cases they are not? What is the rule behind this cases resulting in this behavior?
Thanks in advance!
If the response is not buffered, and the headers are already sent, you can't change them during the end request event, because they have already been written back to the data stream to the browser.
To deal with this, you have a choice:
Turn response buffering on. That way your current code will be able to modify the headers in the buffer (This might not be a great idea if you are serving large documents).
Response.BufferOutput = true;
Instead of handling EndRequest, handle PreSendRequestHeaders instead. This event is guaranteed to fire before the headers are written to the response.
public void Init(HttpApplication context)
{
context.PreSendRequestHeaders += OnEndRequestHandlerExecute;
}
I have a case in ASP.NET,that when a request got, a web page (naming it as service.aspx)will be called asynchronously, and I don't care the response of service.aspx, I just need call it .
Right now ,I have two ways.
First one is HttpWebRequest.BeginGetResponse, parameter callback is set as null. And it seems that it's not a good idea , there is no way to call EndGetResponse, so maybe the resource of calling service.aspx thread could not be released.
Second one is WebClient.OpenReadAsync, but I'm not sure whether it could release thread resource if I don't specify OpenReadCompleted event.
Or maybe there is other appropriate way to have what I want.
You can create a class that will make web requests in the background. Create a static instance of this class in your application's HttpApplication class (Global.asax) and then call a method to queue web requests as required.
Class that performs web requests in the background
public class SiteBackgroundCaller : IRegisteredObject, IDisposable
{
private BlockingCollection<string> requestList;
private CancellationTokenSource queueWorkerCts;
private Task queueWorkerThread;
public SiteBackgroundCaller()
{
// Register an instance of this class with the hosting environment, so we can terminate the task gracefully.
HostingEnvironment.RegisterObject(this);
requestList = new BlockingCollection<string>();
queueWorkerCts = new CancellationTokenSource();
queueWorkerThread = new Task(queueWorkerMethod, TaskCreationOptions.LongRunning);
queueWorkerThread.Start();
}
public void QueueBackgroundRequest(string uri)
{
requestList.Add(uri);
}
private void queueWorkerMethod()
{
while (!queueWorkerCts.IsCancellationRequested)
{
try
{
// This line will block until there is something in the collection
string uri = requestList.Take(queueWorkerCts.Token);
if (queueWorkerCts.IsCancellationRequested)
return;
// Make the request
HttpWebRequest r = (HttpWebRequest)HttpWebRequest.Create(uri);
HttpWebResponse response = (HttpWebResponse)r.GetResponse();
}
catch (OperationCanceledException)
{
// This may throw if the cancellation token is Cancelled.
}
catch (WebException)
{
// Something wrong with the web request (eg timeout)
}
}
}
// Implement IRegisteredObject
public void Stop(bool immediate)
{
queueWorkerCts.Cancel();
queueWorkerThread.Wait();
}
// Implement IDisposable
public void Dispose()
{
HostingEnvironment.UnregisterObject(this);
}
}
Instance of the class in HttpApplication (in Global.asax)
public class Global : System.Web.HttpApplication
{
public static SiteBackgroundCaller BackgroundCaller { get; private set; }
protected void Application_Start(object sender, EventArgs e)
{
BackgroundCaller = new SiteBackgroundCaller();
}
}
Queuing a web request from a page
public partial class MyPage: System.Web.UI.Page
{
protected override void OnLoad(EventArgs e)
{
Global.BackgroundCaller.QueueBackgroundRequest("http://www.example.com/service.aspx");
}
}
You can create the HttpWebRequest and not read the response:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("service.aspx");
using (var response = request.GetResponse())
{
// typically here you would call GetResponseStream and read the content
}
you can also use the async variation:
using (var response = await request.GetResponseAsync())
{
}
I created Global.asax file and put it into my web application, which works on SharePoint 2013.
In Global.asax.cs file, I inherited SPHttpApplication class and override
BeginRequest event. It does not work, the Global.asax file is ignored by SharePoint, or I miss something else to add.
The goal is to create redirection when session expires. What am I doing wrong?
This is my code in code behind:
namespace PhoenixHR.Global
{
public class PhenixHRHttpApplication : SPHttpApplication
{
public override void Init()
{
try
{
base.Init();
this.BeginRequest += new EventHandler(Moj_BeginRequest);
}
catch (Exception ex)
{
}
}
private void Moj_BeginRequest(object sender, EventArgs e)
{
try
{
if (HttpContext.Current.Session["TPL"] == null)
Response.Redirect("~/PhoenixHR/start.aspx");
}
catch (Exception ex)
{
}
}
}
}
Global.asax
<%# Assembly Name="Microsoft.SharePoint"%>
<%# Application Language="C#" Inherits="PhoenixHR.Global.PhenixHRHttpApplication,PhoenixHR.Global,Version=1.0.0.0,Culture=neutral,PublicKeyToken=a0bd8fabe3543dc0" %>
As you have some exception swallowing in your code can you prove the change is not working? or is it raising an exception every request and just ignoring it?
You will be better off with binding to a different event. The HttpApplication class has quite a few events you can bind and BeginRequest is very early (first) in the processing of the request pipeline. This will not have access to the Session State. I would suggest using AcquireRequestState or PostAcquireRequestState.
In order for this to work you will need to create an HttpModule.
namespace PhoenixHR.Global
{
public class SessionMissingRedirectHttpModule : IHttpModule
{
private const string LAYOUTS_SUBFOLDER = "/PhoenixHR.Global";
private const string START_PAGE = "/start.aspx";
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.PostAcquireRequestState += context_PostAcquireRequestState;
}
void context_PostAcquireRequestState(object sender, EventArgs e)
{
// get required contexts
SPHttpApplication application = (SPHttpApplication)sender;
HttpContext context = application.Context;
HttpSessionState session = context.Session;
if (session == null)
return;
if (session["TPL"] != null)
return;
// get SharePoint context and current SPWeb.
SPContext sharepointContext = SPContext.GetContext(context);
SPWeb currentWeb = sharepointContext.Web;
// build a url to the redirect page.
string layoutsFolder =
SPUtility.GetLayoutsFolder(currentWeb);
string url = string.Format("{0}/{1}{2}{3}", currentWeb.Url, layoutsFolder, LAYOUTS_SUBFOLDER, START_PAGE);
// prevent redirection loop
if (context.Request.Url.AbsoluteUri.Equals(url, StringComparison.InvariantCultureIgnoreCase))
return;
SPUtility.Redirect(url, SPRedirectFlags.Trusted, context);
}
}
}
To register the HttpModule you will need to edit the web.config of the Web Application that it will run in.
In the /Configuration/system.webServer/modules path add the following
<add name="SessionMissingRedirectHttpModule" type="PhoenixHR.Global.SessionMissingRedirectHttpModule, PhoenixHR.Global,Version=1.0.0.0,Culture=neutral,PublicKeyToken=a0bd8fabe3543dc0" />
There are some best guesses for paths to the required application pages so you may need to alter the variables to suit.
In my program I'm trying to use session variable in IHttpModule. Here is my code. This is working fine in VS 2010 development server. But when I try to debug in IIS7 it shows exception System.Web.HttpException: Session state is not available in this context
So why session not available in IIS 7 but in development server.
using System;
using System.Web;
public class Globalizer : IHttpModule
{
public void Init(HttpApplication context)
{
context.AcquireRequestState += new EventHandler(setLanguage);
}
public void Dispose(){}
public void setLanguage(Object sender, EventArgs i_eventArgs)
{
HttpApplication http_application = sender as HttpApplication;
http_application.Session["language"] = "test";
}
}
All you need is to implement IRequiresSessionState.
So your code should look like:
public class Globalizer : IHttpModule, IRequiresSessionState
if all you use is reading Sessions (and never update them) then you should use IReadOnlySessionState as this last do not lock the session state, and therefor, you will not have concurrent requests).
If possible, move your handler to a PostAcquireRequestState handler:
Occurs when the request state (for example, session state) that is associated with the current request has been obtained.
(Or an even later event)
The SessionStateModule itself loads session state in response to the AcquireRequestState event - So at the moment, you're depending on whether your module or the session state module's handlers fire first.
Found this over on the ASP.NET forums:
using System;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Diagnostics;
// This code demonstrates how to make session state available in HttpModule,
// regradless of requested resource.
// author: Tomasz Jastrzebski
public class MyHttpModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.PostAcquireRequestState += new EventHandler(Application_PostAcquireRequestState);
application.PostMapRequestHandler += new EventHandler(Application_PostMapRequestHandler);
}
void Application_PostMapRequestHandler(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
if (app.Context.Handler is IReadOnlySessionState || app.Context.Handler is IRequiresSessionState) {
// no need to replace the current handler
return;
}
// swap the current handler
app.Context.Handler = new MyHttpHandler(app.Context.Handler);
}
void Application_PostAcquireRequestState(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
MyHttpHandler resourceHttpHandler = HttpContext.Current.Handler as MyHttpHandler;
if (resourceHttpHandler != null) {
// set the original handler back
HttpContext.Current.Handler = resourceHttpHandler.OriginalHandler;
}
// -> at this point session state should be available
Debug.Assert(app.Session != null, "it did not work :(");
}
public void Dispose()
{
}
// a temp handler used to force the SessionStateModule to load session state
public class MyHttpHandler : IHttpHandler, IRequiresSessionState
{
internal readonly IHttpHandler OriginalHandler;
public MyHttpHandler(IHttpHandler originalHandler)
{
OriginalHandler = originalHandler;
}
public void ProcessRequest(HttpContext context)
{
// do not worry, ProcessRequest() will not be called, but let's be safe
throw new InvalidOperationException("MyHttpHandler cannot process requests.");
}
public bool IsReusable
{
// IsReusable must be set to false since class has a member!
get { return false; }
}
}
}
Found the reason.
If is because AcquireRequestState trigger for the all files such as CSS, JS, images. those files are not having session.
solution: Seems there is a way to avoid calling IHttpModule for all the request. ckeck this answer JS,Images and CSS getting intercepted by HTTPModule.
But it didn't work for me. So I uses HttpContext.Current.Session instead of HttpApplication.Session and every time it checks if Session is null before save to session.
If some one knows, How to avoid calling IHttpModule for files other than .aspx Please help here.
Here is the final code
using System;
using System.Web;
public class Globalizer : IHttpModule
{
public void Init(HttpApplication context)
{
context.AcquireRequestState += new EventHandler(setLanguage);
}
public void Dispose(){}
public void setLanguage(Object sender, EventArgs i_eventArgs)
{
if(HttpContext.Current.Session != null){
HttpContext.Current.Session["language"] = "test";
}
}
}
Edit: One other way is only use session if request comes to a .aspx file
HttpApplication http_application = sender as HttpApplication;
HttpContext context = http_application.Context;
if(Path.GetExtension(context.Request.PhysicalPath) == ".aspx")
{
HttpContext.Current.Session["language"] = "test";
http_application.Session["language2"] = "test2";
}
I'm working on a batch process script that's executed from Flex. The batch script is in a .aspx Page and returns partial results through the following class:
public class ResponseLogger
{
private HttpResponse _response;
public ResponseLogger(HttpResponse response)
{
this._response = response;
}
public void Start()
{
_response.Clear();
_response.ContentType = "text/plain";
_response.ContentEncoding = System.Text.Encoding.GetEncoding("ISO-8859-1");
}
public void End()
{
_response.End();
}
public void Br()
{
Log("");
}
public void Underline(string message)
{
Log(message);
Log("".PadLeft(message.Length, '-'));
}
public void Log(string message)
{
_response.Write(message + "\n");
_response.Flush();
}
}
In my Flex application I'd like to show the result as soon as it's flushed on server side. Can this be done using Actionscript?
Short answer, no, you cannot do partial results over HTTP unless you do short/long polling (several http calls per minute). HTTP in it's essence is a request-response protocol.
What you want is a push technology, but I'm not sure if there's a .NET equivalent for this. On the Java side you got BlazeDS or GraniteDS for push messaging.
The other question is why are you results 'partial'?