A IIIF 2.1 Image API compliant server written as an AWS Serverless Application.
- A simple Lambda Function wrapper for the iiif-processor module.
- A Lambda Function URL that is used to invoke the IIIF API via HTTPS.
- A Lambda Layer containing all the dependencies for the Lambda Function.
- An optional CloudFormation template describing the resources needed to deploy the application.
- Some basic knowledge of AWS.
- An Amazon Web Services account with permissions to create resources via the console and/or command line.
- An Amazon S3 bucket to hold the source images to be served via IIIF. Note: The Lambda Function will be granted read access to this bucket.
serverless-iiif
comes in two flavors: Standalone (Lambda-only) and Caching (CloudFront-enabled). The Standalone version is much simpler, but lacks the following features:
- Custom Domain Name
- Standalone URLs are in the
lambda-url.AWS_REGION.on.aws
domain (e.g.,https://fu90293j0pj902j902c32j902.lambda-url.us-east-1.on.aws/iiif/2/
) - Caching URLs without Custom Domains are in the
cloudfront.net
domain (e.g.,https://d3kmjdzzy1l5t3.cloudfront.net/iiif/2/
)
- Standalone URLs are in the
- Responses larger than ~6MB
- CloudFront function support (for pre/post-processing requests and responses)
serverless-iiif
is distributed and deployed via the AWS Serverless Application Repository. To deploy it using the AWS Console:
- Click one of the following links to deploy the desired application from the AWS Console:
- Make sure your currently selected region (in the console's top navigation bar) is the one you want to deploy to.
- Scroll down to the Application settings section.
- Configure the deploy template:
- Give your stack a unique Application name
- Enter the name of the SourceBucket the service will serve images from
- Check the box acknowledging that the app will create a custom IAM roles and resource policies (and if deploying the Caching version, that it will also deploy a nested application)
- Optional: Enter or change any other parameters that apply to your desired configuration.
- Click Deploy.
- When all the resources are properly created and configured, the new stack should be in the CREATE_COMPLETE stage. If there's an error, it will delete all the resources it created, roll back any changes it made, and eventually reach the ROLLBACK_COMPLETE stage.
- Click the CloudFormation stack link.
- Click the Outputs tab to see (and copy) the IIIF Endpoint URL.
- Make sure you have the SAM CLI and AWS CLI installed.
- Make sure the AWS CLI is properly configured with credentials that have sufficient access to manage IAM, S3, Lambda, and (optionally) CloudFront resources.
- Clone this repository.
- Copy .env.example to .env. Update the various values within.
- Build the application:
$ npm run build
- Deploy the application:
$ npm run deploy
You'll be prompted for various configuration parameters, confirmations, and acknowledgments of specific issues (particularly the creation of IAM resources and the deployment of an open/unauthenticated Lambda Function URL). 7. Follow the prompts to complete the deployment process and get the resulting endpoint.
The easiest way to delete the application is either from the Lambda Applications Console or by deleting its CloudFormation Stack. If you deployed from the command line, you can also use the npm run delete
command.
The S3 key of any given file, minus the extension, is its IIIF ID. For example, if you want to access the image manifest for the file at abcdef.tif
, you would get https://.../iiif/2/abcdef/info.json
. If your key contains slashes, they must be URL-encoded: e.g., ab/cd/ef/gh.tif
would be at https://.../iiif/2/ab%2Fcd%2Fef%2Fgh/info.json
. (This limitation could easily be fixed by encoding only the necessary slashes in the incoming URL before handing it off to the IIIF processor, but that's beyond the scope of the demo.)
iiif-processor
can use any image format natively supported by libvips, including JPEG 2000 (.jp2
), but best results will come from using tiled, multi-resolution TIFFs. The Lambda Function wrapper included in this application assumes a .tif
extension unless you set ResolverTemplate in your .env file.
vips tiffsave source_image.tif output_image.tif --tile --pyramid --compression jpeg --tile-width 256 --tile-height 256
convert source_image.tif -define tiff:tile-geometry=256x256 -compress jpeg 'ptif:output_image.tif'
If tests are run locally they will start in "watch" mode. If a CI environment is detected they will only run once. From the project root run:
npm test
To generate a code coverage report run:
npm test --coverage
This lambda uses the Sharp layer from https://github.com/samvera/lambda-layer-sharp-jp2/releases in order to get a version of Sharp with jp2 support. You can build your own local version using that code and then copy the file to serverless-iiif/sharp-lambda-layer.x86_64.zip. Then set LOCAL_SHARP=true and build/deploy to use your own version.
The SAM deploy template takes several optional parameters to enable the association of CloudFront Functions or Lambda@Edge Functions with the CloudFront distribution. These functions can perform authentication and authorization functions, change how the S3 file and/or image dimensions are resolved, or alter the response from the lambda or cache. These parameters are:
OriginRequestARN
: ARN of the Lambda@Edge Function to use at the origin-request stageOriginResponseARN
: ARN of the Lambda@Edge Function to use at the origin-response stageViewerRequestARN
: ARN of the CloudFront or Lambda@Edge Function to use at the viewer-request stageViewerRequestType
: Type of viewer-request Function to use (CloudWatch Function
orLambda@Edge
)ViewerResponseARN
: ARN of the CloudFront or Lambda@Edge Function to use at the viewer-response stageViewerResponseType
: Type of viewer-response Function to use (CloudWatch Function
orLambda@Edge
)
These functions, if used, must be created, configured, and published before the serverless application is deployed.
These examples use CloudFront Functions. Lambda@Edge functions are slightly more complicated in terms of the event structure but the basic idea is the same.
function handler(event) {
if (notAuthorized) { // based on something in the event.request
return {
statusCode: 403,
statusDescription: 'Unauthorized'
};
};
return event.request;
}
function handler(event) {
var request = event.request;
request.headers['x-preflight-location'] = {value: 's3://image-bucket/path/to/correct/image.tif'};
request.headers['x-preflight-dimensions'] = {value: JSON.stringify({ width: 640, height: 480 })};
return request;
}
Note: The SAM deploy template adds a preflight=true
environment variable to the main IIIF Lambda if a preflight function is provided. The function will only look for the preflight headers if this environment variable is true
. This prevents requests from including those headers directly if no preflight function is present. If you do use a preflight function, make sure it strips out any x-preflight-location
and x-preflight-dimensions
headers that it doesn't set itself.
Lambda Function URLs have a payload (request/response body) size limit of approximately 6MB in both directions. To overcome this limitation, the Lambda URL is configured behind an AWS CloudFront distribution with two origins - the API and a cache bucket. Responses larger than 6MB are saved to the cache bucket at the same relative path as the request, and the Lambda returns a 404 Not Found
response to CloudFront. CloudFront then fails over to the second origin (the cache bucket), where it finds the actual response and returns it.
The cache bucket uses an S3 lifecycle rule to expire cached responses in 1 day.
serverless-iiif
is available under the Apache 2.0 license.
If you're working on a PR for this project, create a feature branch off of main
.
This repository follows the Samvera Community Code of Conduct and language recommendations. Please do not create a branch called master
for this repository or as part of your pull request; the branch will either need to be removed or renamed before it can be considered for inclusion in the code base and history of this repository.