Skip to content

Doing More Stuff

Cătălin Stan edited this page Jul 26, 2016 · 6 revisions

Our app is now able to perform a basic set of functions but it is far from being ready for deployment in the wilderness of “Hello World!”-craving enthusiasts. Let’s look at some other real world scenarios.

If you don’t know what I’m on about, check the “Getting Started” guide.

Serving Static Files from a Directory

It is conceivable that you will need to serve a file from disk at some point.

- (void)mount:(NSString *)path directoryAtPath:(NSString *)directoryPath options:(CRStaticDirectoryServingOptions)options;

This method allows “mounting” directories as static file serving points. The options parameter is an bitwise-or’ed list of CRStaticDirectoryServingOptions values.

  • CRStaticDirectoryServingOptionsCacheFiles - use the OS’s disk cache when serving files. (this only applies to files smaller that 512K).
  • CRStaticDirectoryServingOptionsFollowSymlinks - follow symbolic-links.
  • CRStaticDirectoryServingOptionsAutoIndex - generates a clickable HTML index of the directory’s contents.
  • CRStaticDirectoryServingOptionsAutoIndexShowHidden - show hidden files in the auto-generated index.

Mounting the Directory

We will add a route that serves everything in the current user’s home directory as an auto-generated index, with caching. We’ll add this block before the logging block, in order to give it a chance to set the response headers before we try to log them.

[self.server mount:@"/pub" directoryAtPath:@"~" options:CRStaticDirectoryServingOptionsCacheFiles|CRStaticDirectoryServingOptionsAutoIndex];

You should be able to see the file list at http://localhost:10781/pub.

CRStaticDirectoryManager

The class responsible for serving the files is CRStaticDirectoryManager.

Static files under 512K are read entirely in memory - using or not the OS disk cache, depending on the CRStaticDirectoryServingOptionsCacheFiles flag - synchronously within the same context of the route blocks.

Static files over that threshold are read in chunks using the Dispatch I/O Channel API on a completely separate queue. CRStaticDirectoryManager’s route block will call its completionHandler() as soon as the I/O channel has been opened and the reading has been queued. In our case, if you are serving a large file, the logging block will be executed before the reading of the file is completed.

MIME-Type Hinting

CRStaticDirectoryManager uses CRMimeTypeHelper in order to determine the value of the Content-type HTTP header. It uses the files UTI in order to determine the appropriate value.

While this algorithm is evolving, you may also specify an explicit value you want to set for a specific extension.

Let’s specify that .nfo files will have the Content-Type HTTP header set to text/plain; charset-utf-8.

[[CRMimeTypeHelper sharedHelper] setMimeType:@"text/plain; charset=utf-8" forExtension:@"nfo"];

Serving a Single Static File

(to be continued)

Cookies

Criollo uses NSHTTPCookie class to encapsulate HTTP cookies. Criollo adds a category to the NSHTTPCookie class, that defines the method responseHeaderFieldsWithCookies: in order to create the appropriate response headers.

Criollo manages the cookie store for you, so you don’t have to worry about persisting, deleting expired cookies, etc.

We will create two cookies.

  • a session cookie called session, which will contain a UUID
  • a long lived cookie called token, which will expire a long way into the future

For simplicity, we will add this code to the block that sets the Server HTTP header.

if ( !request.cookies[@"session"] ) {
	[response setCookie:@"session" value:[NSUUID UUID].UUIDString path:@"/" expires:nil domain:nil secure:NO];
}
[response setCookie:@"token" value:[NSUUID UUID].UUIDString path:@"/" expires:[NSDate distantFuture] domain:nil secure:NO];

The CRRequest and CRResponse cookie APIs are straight-forward.

@property (nonatomic, readonly, nonnull) NSDictionary<NSString *, NSString *> * cookies;
- (void)setCookie:(nonnull NSHTTPCookie *)cookie;
- (nonnull NSHTTPCookie*)setCookie:(nonnull NSString *)name value:(nonnull NSString *)value path:(nonnull NSString *)path expires:(nullable NSDate *)expires domain:(nullable NSString *)domain secure:(BOOL)secure;

Setting up a Route Controller

(to be continued)

Setting up a View Controller

(to be continued)

Logging and CRServerDelegate

(to be continued)

Clean Start and Shutdown

(to be continued)