function website(request, log, callback) {
    if (request.method === 'HEAD') {
        // eslint-disable-next-line no-param-reassign
        callback = callbackGetToHead(callback);
    }
    const methodCapitalized = capitalize(request.method);
    const action = request.method === 'HEAD' ? 'headObject' : 'getObject';
    log.debug('processing request', { method: `website${methodCapitalized}` });
    const bucketName = request.bucketName;
    const reqObjectKey = request.objectKey ? request.objectKey : '';

    return metadata.getBucket(bucketName, log, (err, bucket) => {
        if (err) {
            log.trace('error retrieving bucket metadata', { error: err });
            monitoring.promMetrics(
                request.method, bucketName, err.code, action);
            return callback(err, false);
        }
        if (bucketShield(bucket, `object${methodCapitalized}`)) {
            log.trace('bucket in transient/deleted state so shielding');
            monitoring.promMetrics(
                request.method, bucketName, 404, action);
            return callback(errors.NoSuchBucket, false);
        }
        const corsHeaders = collectCorsHeaders(request.headers.origin,
            request.method, bucket);
        // bucket ACL's do not matter for website head since it is always the
        // head of an object. object ACL's are what matter
        const websiteConfig = bucket.getWebsiteConfiguration();
        if (!websiteConfig) {
            monitoring.promMetrics(
                request.method, bucketName, 404, action);
            return callback(errors.NoSuchWebsiteConfiguration, false, null,
            corsHeaders);
        }
        // any errors above would be our own created generic error html
        // if have a website config, error going forward would be user's
        // redirect or error page if they set either in the config

        // handle redirect all
        if (websiteConfig.getRedirectAllRequestsTo()) {
            return callback(null, false, null, corsHeaders,
                websiteConfig.getRedirectAllRequestsTo(), reqObjectKey);
        }

        // check whether need to redirect based on key
        const routingRules = websiteConfig.getRoutingRules();
        const keyRoutingRule = findRoutingRule(routingRules, reqObjectKey);

        if (keyRoutingRule) {
            // TODO: optimize by not rerouting if only routing
            // rule is to change out key
            return callback(null, false, null, corsHeaders,
                keyRoutingRule, reqObjectKey);
        }

        appendWebsiteIndexDocument(request, websiteConfig.getIndexDocument());

        /**
         * Recursive function with 1 recursive call to look for index
         * in case of error for potential redirect to folder notation
         * if there is not already an index appended
         * @param {Error} [originalError] - presence of this argument
         * differentiates original user request from recursive call to /index.
         * This error is returned if /index is not found
         * @returns {undefined}
         */
        function runWebsite(originalError) {
            // get object metadata and check authorization and header
            // validation
            return metadata.getObjectMD(bucketName, request.objectKey, {}, log,
                (err, objMD) => {
                    // Note: In case of error, we intentionally send the original
                    // object key to _errorActions as in case of a redirect, we do
                    // not want to append index key to redirect location
                    if (err) {
                        log.trace('error retrieving object metadata',
                        { error: err });
                        let returnErr = err;
                        const bucketAuthorized = isBucketAuthorized(bucket, request.apiMethods || 'bucketGet',
                        constants.publicId, null, log, request, request.actionImplicitDenies, true);
                        // if index object does not exist and bucket is private AWS
                        // returns 403 - AccessDenied error.
                        if (err.is.NoSuchKey && !bucketAuthorized) {
                            returnErr = errors.AccessDenied;
                        }

                        // Check if key is a folder containing index for redirect 302
                        // https://docs.aws.amazon.com/AmazonS3/latest/userguide/IndexDocumentSupport.html
                        if (!originalError && reqObjectKey && !reqObjectKey.endsWith('/')) {
                            appendWebsiteIndexDocument(request, websiteConfig.getIndexDocument(), true);
                            // propagate returnErr as originalError to be used if index is not found
                            return runWebsite(returnErr);
                        }

                        return _errorActions(originalError || returnErr,
                        websiteConfig.getErrorDocument(), routingRules,
                        bucket, reqObjectKey, corsHeaders, request, log,
                        callback);
                    }
                    if (!isObjAuthorized(bucket, objMD, request.apiMethods || 'objectGet',
                        constants.publicId, null, log, request, request.actionImplicitDenies, true)) {
                        const err = errors.AccessDenied;
                        log.trace('request not authorized', { error: err });
                        return _errorActions(err, websiteConfig.getErrorDocument(),
                            routingRules, bucket,
                            reqObjectKey, corsHeaders, request, log, callback);
                    }

                    // access granted to index document, needs a redirect 302
                    // to the original key with trailing /
                    if (originalError) {
                        const redirectInfo = { withError: true,
                            location: `/${reqObjectKey}/` };
                        return callback(errors.Found, false, null, corsHeaders,
                            redirectInfo, '');
                    }

                    const headerValResult = validateHeaders(request.headers,
                        objMD['last-modified'], objMD['content-md5']);
                    if (headerValResult.error) {
                        const err = headerValResult.error;
                        log.trace('header validation error', { error: err });
                        return _errorActions(err, websiteConfig.getErrorDocument(),
                            routingRules, bucket, reqObjectKey,
                            corsHeaders, request, log, callback);
                    }
                    // check if object to serve has website redirect header
                    // Note: AWS prioritizes website configuration rules over
                    // object key's website redirect header, so we make the
                    // check at the end.
                    if (objMD['x-amz-website-redirect-location']) {
                        const redirectLocation =
                            objMD['x-amz-website-redirect-location'];
                        const redirectInfo =
                            extractRedirectInfo(redirectLocation);
                        log.trace('redirecting to x-amz-website-redirect-location',
                            { location: redirectLocation });
                        return callback(null, false, null, corsHeaders,
                            redirectInfo, '');
                    }
                    // got obj metadata, authorized and headers validated,
                    // good to go
                    const responseMetaHeaders = collectResponseHeaders(objMD,
                        corsHeaders);

                    if (request.method === 'HEAD') {
                        pushMetric('headObject', log, { bucket: bucketName });
                        monitoring.promMetrics('HEAD',
                            bucketName, '200', 'headObject');
                        return callback(null, false, null, responseMetaHeaders);
                    }

                    const dataLocator = objMD.location;
                    if (objMD['x-amz-server-side-encryption']) {
                        for (let i = 0; i < dataLocator.length; i++) {
                            dataLocator[i].masterKeyId =
                                objMD['x-amz-server-side-encryption-aws-' +
                                    'kms-key-id'];
                            dataLocator[i].algorithm =
                                objMD['x-amz-server-side-encryption'];
                        }
                    }
                    pushMetric('getObject', log, {
                        bucket: bucketName,
                        newByteLength: responseMetaHeaders['Content-Length'],
                    });
                    monitoring.promMetrics('GET', bucketName, '200',
                        'getObject', responseMetaHeaders['Content-Length']);
                    return callback(null, false, dataLocator, responseMetaHeaders);
                });
        }

        return runWebsite();
    });
}
Website - Common website function for GET and HEAD
Gets metadata and object for website or redirects
@param {object} request - normalized request object
@param {object} log - Werelogs instance
@param {function} callback - callback to function in route
@return {undefined}