ImageResizer  3.4.0
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Events
Protected Member Functions | Properties | List of all members
ImageResizer.InterceptModule Class Reference

Monitors incoming image requests to determine if resizing (or other processing) is being requested. More...

Inheritance diagram for ImageResizer.InterceptModule:
Inheritance graph
[legend]
Collaboration diagram for ImageResizer.InterceptModule:
Collaboration graph
[legend]

Protected Member Functions

virtual void CheckRequest_PostAuthorizeRequest (object sender, EventArgs e)
 This is where we filter requests and intercept those that want resizing performed. We first strip FakeExtension, then verify the remaining file extension is supported for decoding. We fire URL rewriting events. If the result includes any supported querystring params afterwards, we process the request. Otherwise we let it fall back to IIS/ASP.NET. If the file doesn't exist, we also ignore the request. They're going to cause a 404 anyway. More...
 
void FileMissing (HttpContext httpContext, string virtualPath, NameValueCollection q)
 
virtual void HandleRequest (HttpContext context, string virtualPath, NameValueCollection queryString, IVirtualFile vf)
 Generates the resized image to disk (if needed), then rewrites the request to that location. Perform 404 checking before calling this method. Assumes file exists. Called during PostAuthorizeRequest More...
 
void context_PreSendRequestHeaders (object sender, EventArgs e)
 We don't actually send the data - but we still want to control the headers on the data. PreSendRequestHeaders allows us to change the content-type and cache headers at excatly the last More...
 

Properties

IPipelineConfig conf [get]
 Current configuration. Same as Config.Current.Pipeline More...
 

Detailed Description

Monitors incoming image requests to determine if resizing (or other processing) is being requested.

Definition at line 26 of file InterceptModule.cs.

Member Function Documentation

virtual void ImageResizer.InterceptModule.CheckRequest_PostAuthorizeRequest ( object  sender,
EventArgs  e 
)
inlineprotectedvirtual

This is where we filter requests and intercept those that want resizing performed. We first strip FakeExtension, then verify the remaining file extension is supported for decoding. We fire URL rewriting events. If the result includes any supported querystring params afterwards, we process the request. Otherwise we let it fall back to IIS/ASP.NET. If the file doesn't exist, we also ignore the request. They're going to cause a 404 anyway.

Parameters
sender
e

Definition at line 62 of file InterceptModule.cs.

62  {
63  //Skip requests if the Request object isn't populated
64  HttpApplication app = sender as HttpApplication;
65  if (app == null) return;
66  if (app.Context == null) return;
67  if (app.Context.Request == null) return;
68 
69  conf.FirePostAuthorizeRequest(this, app.Context);
70 
71 
72 
73  //Allow handlers of the above event to change filePath/pathinfo so we can successfull test the extension
74  string originalPath = conf.PreRewritePath;
75 
76  //Trim fake extensions so IsAcceptedImageType will work properly
77  string filePath = conf.TrimFakeExtensions(originalPath);
78 
79 
80  //Is this an image request? Checks the file extension for .jpg, .png, .tiff, etc.
82  //Copy the querystring so we can mod it to death without messing up other stuff.
83  NameValueCollection q = conf.ModifiedQueryString;
84 
85  //Call URL rewriting events
86  UrlEventArgs ue = new UrlEventArgs(filePath, q);
87  conf.FireRewritingEvents(this, app.Context,ue);
88 
89  //Pull data back out of event object, resolving app-relative paths
90  string virtualPath = PathUtils.ResolveAppRelativeAssumeAppRelative(ue.VirtualPath);
91  q = ue.QueryString;
92 
93  //Store the modified querystring in request for use by VirtualPathProviders
94  conf.ModifiedQueryString = q; // app.Context.Items["modifiedQueryString"] = q;
95 
96  //See if resizing is wanted (i.e. one of the querystring commands is present).
97  //Called after processPath so processPath can add them if needed.
98  //Checks for thumnail, format, width, height, maxwidth, maxheight and a lot more
99  if (conf.HasPipelineDirective(q)) {
100  //Who's the user
101  IPrincipal user = app.Context.User as IPrincipal;
102 
103  // no user (must be anonymous...).. Or authentication doesn't work for this suffix. Whatever, just avoid a nullref in the UrlAuthorizationModule
104  if (user == null)
105  user = new GenericPrincipal(new GenericIdentity(string.Empty, string.Empty), new string[0]);
106 
107  //Do we have permission to call UrlAuthorizationModule.CheckUrlAccessForPrincipal?
108  bool canCheckUrl = System.Security.SecurityManager.IsGranted(new System.Security.Permissions.SecurityPermission(System.Security.Permissions.PermissionState.Unrestricted));
109 
110 
111  //Run the rewritten path past the auth system again, using the result as the default "AllowAccess" value
112  bool isAllowed = true;
113  if (canCheckUrl && !app.Context.SkipAuthorization) try {
114  isAllowed = UrlAuthorizationModule.CheckUrlAccessForPrincipal(virtualPath, user, "GET");
115  } catch (NotImplementedException) { } //For MONO support
116 
117 
118  IUrlAuthorizationEventArgs authEvent = new UrlAuthorizationEventArgs(virtualPath, new NameValueCollection(q), isAllowed);
119 
120  //Allow user code to deny access, but not modify the url or querystring.
121  conf.FireAuthorizeImage(this, app.Context, authEvent);
122 
123  if (!authEvent.AllowAccess) throw new ImageProcessingException(403, "Access denied", "Access denied");
124 
125 
126 
127  //Does the file exist physically? (false if VppUsage=always or file is missing)
128  bool existsPhysically = (conf.VppUsage != VppUsageOption.Always) && System.IO.File.Exists(HostingEnvironment.MapPath(virtualPath));
129 
130  //If not present physically (and VppUsage!=never), try to get the virtual file. Null indicates a missing file
131  IVirtualFile vf = (conf.VppUsage != VppUsageOption.Never && !existsPhysically) ? conf.GetFile(virtualPath, q) : null;
132 
133  //Only process files that exist
134  if (existsPhysically || vf != null) {
135  try{
136  HandleRequest(app.Context, virtualPath, q, vf);
137  //Catch not found exceptions
138  } catch (System.IO.FileNotFoundException notFound) { //Some VPPs are optimisitic , or could be a race condition
139  FileMissing(app.Context, virtualPath, q);
140  throw new ImageMissingException("The specified resource could not be located", "File not found", notFound);
141  }
142  } else
143  FileMissing(app.Context, virtualPath, q);
144 
145  }
146  }
147 
148  }
new IVirtualFile GetFile(string virtualPath, NameValueCollection queryString)
A virtual file to support IVirtualImageProvider
Definition: IVirtualFile.cs:10
bool SkipFileTypeCheck
Get or sets whether the file extension check should be applied to the current request. Defaults to true. If set to true, will only affect the current request, and will only cause the Resizer to evaluate the rewriting rules on the request. Processing may still not occur if no querystring values are specified. Add 'cache=always' to force caching to occur.
IPipelineConfig conf
Current configuration. Same as Config.Current.Pipeline
virtual void HandleRequest(HttpContext context, string virtualPath, NameValueCollection queryString, IVirtualFile vf)
Generates the resized image to disk (if needed), then rewrites the request to that location...
bool HasPipelineDirective(NameValueCollection q)
True if the querystring contains any directives that are understood by the pipeline ...
bool IsAcceptedImageType(string filePath)
True if the specified extension is one that the pipeline can handle
void ImageResizer.InterceptModule.context_PreSendRequestHeaders ( object  sender,
EventArgs  e 
)
inlineprotected

We don't actually send the data - but we still want to control the headers on the data. PreSendRequestHeaders allows us to change the content-type and cache headers at excatly the last

Parameters
sender
e

Definition at line 312 of file InterceptModule.cs.

312  {
313  //Skip requests if the Request object isn't populated
314  HttpApplication app = sender as HttpApplication;
315  if (app == null) return;
316  if (app.Context == null) return;
317  if (app.Context.Items == null) return;
318  if (app.Context.Request == null) return;
319  HttpContext context = app.Context;
320  //Skip requests if we don't have an object to work with.
321  if (context.Items[conf.ResponseArgsKey] == null) return;
322  //Try to cast the object.
323  IResponseArgs obj = context.Items[conf.ResponseArgsKey] as IResponseArgs;
324  if (obj == null) return;
325  //Apply the headers
327  obj.ResponseHeaders.ApplyToResponse(obj.ResponseHeaders, app.Context);
328 
329 
330  }
IResponseHeaders ResponseHeaders
The content-type of the data, among other things. Set ResponseHeaders.ApplyDuringPreSendRequestHeader...
IPipelineConfig conf
Current configuration. Same as Config.Current.Pipeline
bool ApplyDuringPreSendRequestHeaders
True if the application should automatically execute ApplyToResponse() during the PreSendRequestHeade...
string ResponseArgsKey
The key in Context.Items to store the IResponseArgs object
A collection of data and callbacks that can be passed to a caching object.
virtual void ImageResizer.InterceptModule.HandleRequest ( HttpContext  context,
string  virtualPath,
NameValueCollection  queryString,
IVirtualFile  vf 
)
inlineprotectedvirtual

Generates the resized image to disk (if needed), then rewrites the request to that location. Perform 404 checking before calling this method. Assumes file exists. Called during PostAuthorizeRequest

Parameters
context
virtualPath
queryString
vf

Definition at line 171 of file InterceptModule.cs.

Referenced by ImageResizer.InterceptModule.CheckRequest_PostAuthorizeRequest().

171  {
172  Stopwatch s = new Stopwatch();
173  s.Start();
174 
175 
176  ResizeSettings settings = new ResizeSettings(queryString);
177 
178  bool isCaching = settings.Cache == ServerCacheMode.Always;
179  bool isProcessing = settings.Process == ProcessWhen.Always;
180 
181  //By default, we process it if is both (a) a recognized image extension, and (b) has a resizing directive (not just 'cache').
182  if (settings.Process == ProcessWhen.Default){
183  //Check for resize directive by removing ('non-resizing' items from the current querystring)
184  NameValueCollection copy = new NameValueCollection(queryString);
185  copy.Remove("cache"); copy.Remove("process"); copy.Remove("useresizingpipeline"); copy.Remove("404");
186  //If the 'copy' still has directives, and it's an image request, then let's process it.
187  isProcessing = conf.IsAcceptedImageType(virtualPath) && conf.HasPipelineDirective(copy);
188  }
189 
190  //By default, we only cache it if we're processing it.
191  if (settings.Cache == ServerCacheMode.Default && isProcessing)
192  isCaching = true;
193 
194  //Resolve the 'cache' setting to 'no' unless we want it cache.
195  if (!isCaching) settings.Cache = ServerCacheMode.No;
196 
197 
198  //If we are neither processing nor caching, don't do anything more with the request
199  if (!isProcessing && !isCaching) return;
200  context.Items[conf.ResponseArgsKey] = ""; //We are handling the requests
201 
202  //Communicate to the MVC plugin this request should not be affected by the UrlRoutingModule.
203  context.Items[conf.StopRoutingKey] = true;
204 
205  //Find out if we have a modified date that we can work with
206  bool hasModifiedDate = (vf == null) || vf is IVirtualFileWithModifiedDate;
207  DateTime modDate = DateTime.MinValue;
208  if (hasModifiedDate && vf != null) {
209  modDate = ((IVirtualFileWithModifiedDate)vf).ModifiedDateUTC;
210  if (modDate == DateTime.MinValue || modDate == DateTime.MaxValue) {
211  hasModifiedDate = false; //Skip modified date checking if the file has no modified date
212  }
213  }
214 
215 
216  IEncoder guessedEncoder = null;
217  //Only use an encoder to determine extension/mime-type when it's an image extension or when we've set process = always.
218  if (isProcessing) {
219  guessedEncoder = conf.GetImageBuilder().EncoderProvider.GetEncoder(settings, virtualPath);
220  if (guessedEncoder == null) throw new ImageProcessingException("Image Resizer: No image encoder was found for the request.");
221  }
222 
223  //Determine the file extenson for the caching system to use if we aren't processing the image
224  //Use the exsiting one if is an image extension. If not, use "unknown".
225  // We don't want to suggest writing .exe or .aspx files to the cache!
226  string fallbackExtension = PathUtils.GetFullExtension(virtualPath).TrimStart('.');
227  if (!conf.IsAcceptedImageType(virtualPath)) fallbackExtension = "unknown";
228 
229  //Determine the mime-type if we aren't processing the image.
230  string fallbackContentType = "application/octet-stream";
231  //Support jpeg, png, gif, bmp, tiff mime-types. Otherwise use "application/octet-stream".
232  //We can't set it to null - it will default to text/html
233  System.Drawing.Imaging.ImageFormat recognizedExtension = DefaultEncoder.GetImageFormatFromExtension(fallbackExtension);
234  if (recognizedExtension != null) fallbackContentType = DefaultEncoder.GetContentTypeFromImageFormat(recognizedExtension);
235 
236 
237  //Build CacheEventArgs
238  ResponseArgs e = new ResponseArgs();
239  e.RequestKey = virtualPath + PathUtils.BuildQueryString(queryString);
240  e.RewrittenQuerystring = settings;
241  e.ResponseHeaders.ContentType = isProcessing ? guessedEncoder.MimeType : fallbackContentType;
242  e.SuggestedExtension = isProcessing ? guessedEncoder.Extension : fallbackExtension;
243  e.HasModifiedDate = hasModifiedDate;
244  //Add delegate for retrieving the modified date of the source file.
245  e.GetModifiedDateUTC = new ModifiedDateDelegate(delegate() {
246  if (vf == null)
247  return System.IO.File.GetLastWriteTimeUtc(HostingEnvironment.MapPath(virtualPath));
248  else if (hasModifiedDate)
249  return modDate;
250  else return DateTime.MinValue; //Won't be called, no modified date available.
251  });
252 
253  //A delegate for accessing the source file
254  e.GetSourceImage = new GetSourceImageDelegate(delegate() {
255  return (vf != null) ? vf.Open() : File.Open(HostingEnvironment.MapPath(virtualPath), FileMode.Open, FileAccess.Read, FileShare.Read);
256  });
257 
258  //Add delegate for writing the data stream
259  e.ResizeImageToStream = new ResizeImageDelegate(delegate(System.IO.Stream stream) {
260  //This runs on a cache miss or cache invalid. This delegate is preventing from running in more
261  //than one thread at a time for the specified cache key
262  try {
263  if (!isProcessing) {
264  //Just duplicate the data
265  using (Stream source = e.GetSourceImage())
266  StreamExtensions.CopyToStream(source, stream); //4KiB buffer
267 
268  } else {
269  //Process the image
270  if (vf != null)
271  conf.GetImageBuilder().Build(vf, stream, settings);
272  else
273  conf.GetImageBuilder().Build(HostingEnvironment.MapPath(virtualPath), stream, settings); //Use a physical path to bypass virtual file system
274  }
275 
276  //Catch not found exceptions
277  } catch (System.IO.FileNotFoundException notFound) {
278  //This will be called later, if at all.
279  FileMissing(context, virtualPath, queryString);
280  throw new ImageMissingException("The specified resource could not be located","File not found", notFound);
281  }
282  });
283 
284 
285  context.Items[conf.ResponseArgsKey] = e; //store in context items
286 
287  //Fire events (for client-side caching plugins)
288  conf.FirePreHandleImage(this, context, e);
289 
290  //Pass the rest of the work off to the caching module. It will handle rewriting/redirecting and everything.
291  //We handle request headers based on what is found in context.Items
292  ICache cache = conf.GetCacheProvider().GetCachingSystem(context, e);
293 
294  //Verify we have a caching system
295  if (cache == null) throw new ImageProcessingException("Image Resizer: No caching plugin was found for the request");
296 
297  cache.Process(context, e);
298 
299 
300 
301  s.Stop();
302  context.Items["ResizingTime"] = s.ElapsedMilliseconds;
303 
304  }
Provides caching behavior
Definition: ICache.cs:12
delegate DateTime ModifiedDateDelegate()
A callback method to return the last modified date of the source file if available, or DateTime.MinValue if not available.
IResponseArgs implementation
Definition: ResponseArgs.cs:12
IPipelineConfig conf
Current configuration. Same as Config.Current.Pipeline
delegate void ResizeImageDelegate(Stream s)
A callback method that will resize, encode, and write the data to the given stream. Callback may throw FileNotFoundException when running on top of an optimistic VPP
bool HasPipelineDirective(NameValueCollection q)
True if the querystring contains any directives that are understood by the pipeline ...
ProcessWhen
When to process and re-encode the file.
Definition: Enumerations.cs:54
ServerCacheMode
When to disk cache the image
Definition: Enumerations.cs:36
bool IsAcceptedImageType(string filePath)
True if the specified extension is one that the pipeline can handle
Stream Open()
Returns an opened stream to the file contents.
An image encoder. Exposes methods for suitability checking, encoding, transparency compatibility chec...
Definition: IEncoder.cs:13
Always implement this if possible. Allows caching systems to detect changes to source files and inval...

Property Documentation

IPipelineConfig ImageResizer.InterceptModule.conf
getprotected

Current configuration. Same as Config.Current.Pipeline

Definition at line 51 of file InterceptModule.cs.

Referenced by ImageResizer.InterceptModule.CheckRequest_PostAuthorizeRequest(), and ImageResizer.InterceptModule.HandleRequest().


The documentation for this class was generated from the following file: