Lambda@Edge 함수 예제 - Amazon CloudFront

Lambda@Edge 함수 예제

CloudFront에서 Lambda 함수를 사용하는 예제는 다음 단원을 참조하십시오.

참고

Node.js 함수의 경우 각 함수는 callback 파라미터를 호출하여 요청을 성공적으로 처리하거나 응답을 반환해야 합니다. 자세한 내용은 Lambda@Edge 함수 작성 및 생성 단원을 참조하십시오.

일반 예제

이 섹션의 예제는 CloudFront에서 Lambda@Edge를 사용하는 몇 가지 일반적인 방법을 보여줍니다.

예: A/B 테스트

다음 예제를 사용하여 리디렉션을 만들거나 URL을 변경하지 않고 두 가지 다른 버전의 이미지를 테스트할 수 있습니다. 이 예제는 최종 사용자 요청의 쿠키를 읽고 그에 따라 요청 URL을 수정합니다. 최종 사용자가 예상 값 중 하나와 함께 쿠키를 보내지 않는 경우, 이 예제에서는 최종 사용자를 URL 중 하나에 임의로 할당합니다.

Node.js
'use strict'; exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const headers = request.headers; if (request.uri !== '/experiment-pixel.jpg') { // do not process if this is not an A-B test request callback(null, request); return; } const cookieExperimentA = 'X-Experiment-Name=A'; const cookieExperimentB = 'X-Experiment-Name=B'; const pathExperimentA = '/experiment-group/control-pixel.jpg'; const pathExperimentB = '/experiment-group/treatment-pixel.jpg'; /* * Lambda at the Edge headers are array objects. * * Client may send multiple Cookie headers, i.e.: * > GET /viewerRes/test HTTP/1.1 * > User-Agent: curl/7.18.1 (x86_64-unknown-linux-gnu) libcurl/7.18.1 OpenSSL/1.0.1u zlib/1.2.3 * > Cookie: First=1; Second=2 * > Cookie: ClientCode=abc * > Host: example.com * * You can access the first Cookie header at headers["cookie"][0].value * and the second at headers["cookie"][1].value. * * Header values are not parsed. In the example above, * headers["cookie"][0].value is equal to "First=1; Second=2" */ let experimentUri; if (headers.cookie) { for (let i = 0; i < headers.cookie.length; i++) { if (headers.cookie[i].value.indexOf(cookieExperimentA) >= 0) { console.log('Experiment A cookie found'); experimentUri = pathExperimentA; break; } else if (headers.cookie[i].value.indexOf(cookieExperimentB) >= 0) { console.log('Experiment B cookie found'); experimentUri = pathExperimentB; break; } } } if (!experimentUri) { console.log('Experiment cookie has not been found. Throwing dice...'); if (Math.random() < 0.75) { experimentUri = pathExperimentA; } else { experimentUri = pathExperimentB; } } request.uri = experimentUri; console.log(`Request uri set to "${request.uri}"`); callback(null, request); };
Python
import json import random def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] headers = request['headers'] if request['uri'] != '/experiment-pixel.jpg': # Not an A/B Test return request cookieExperimentA, cookieExperimentB = 'X-Experiment-Name=A', 'X-Experiment-Name=B' pathExperimentA, pathExperimentB = '/experiment-group/control-pixel.jpg', '/experiment-group/treatment-pixel.jpg' ''' Lambda at the Edge headers are array objects. Client may send multiple cookie headers. For example: > GET /viewerRes/test HTTP/1.1 > User-Agent: curl/7.18.1 (x86_64-unknown-linux-gnu) libcurl/7.18.1 OpenSSL/1.0.1u zlib/1.2.3 > Cookie: First=1; Second=2 > Cookie: ClientCode=abc > Host: example.com You can access the first Cookie header at headers["cookie"][0].value and the second at headers["cookie"][1].value. Header values are not parsed. In the example above, headers["cookie"][0].value is equal to "First=1; Second=2" ''' experimentUri = "" for cookie in headers.get('cookie', []): if cookieExperimentA in cookie['value']: print("Experiment A cookie found") experimentUri = pathExperimentA break elif cookieExperimentB in cookie['value']: print("Experiment B cookie found") experimentUri = pathExperimentB break if not experimentUri: print("Experiment cookie has not been found. Throwing dice...") if random.random() < 0.75: experimentUri = pathExperimentA else: experimentUri = pathExperimentB request['uri'] = experimentUri print(f"Request uri set to {experimentUri}") return request

예제: 응답 헤더 재정의

다음 예제는 다른 헤더의 값을 기준으로 응답 헤더의 값을 변경하는 방법을 보여줍니다.

Node.js
'use strict'; exports.handler = (event, context, callback) => { const response = event.Records[0].cf.response; const headers = response.headers; const headerNameSrc = 'X-Amz-Meta-Last-Modified'; const headerNameDst = 'Last-Modified'; if (headers[headerNameSrc.toLowerCase()]) { headers[headerNameDst.toLowerCase()] = [ headers[headerNameSrc.toLowerCase()][0], ]; console.log(`Response header "${headerNameDst}" was set to ` + `"${headers[headerNameDst.toLowerCase()][0].value}"`); } callback(null, response); };
Python
import json def lambda_handler(event, context): response = event["Records"][0]["cf"]["response"] headers = response["headers"] headerNameSrc = "X-Amz-Meta-Last-Modified" headerNameDst = "Last-Modified" if headers.get(headerNameSrc.lower(), None): headers[headerNameDst.lower()] = [headers[headerNameSrc.lower()][0]] print(f"Response header {headerNameDst.lower()} was set to {headers[headerNameSrc.lower()][0]}") return response

응답 생성 - 예제

이 섹션의 예제는 Lambda@Edge를 사용하여 응답을 생성하는 방법을 보여줍니다.

예제: 정적 콘텐츠 서비스(생성된 응답)

다음 예제는 Lambda 함수를 사용하여 정적 웹사이트 콘텐츠를 서비스하는 방법을 보여줍니다. 이는 오리진 서버에 대한 로드를 줄이고 전체 지연 시간을 단축합니다.

참고

최종 사용자 요청 및 오리진 요청 이벤트에 대해 HTTP 응답을 만들 수 있습니다. 자세한 내용은 요청 트리거에서 HTTP 응답 생성 단원을 참조하십시오. 오리진 및 최종 사용자 응답 이벤트에서 HTTP 응답을 바꿀 수도 있습니다. 자세한 내용은 오리진 응답 트리거에서 HTTP 응답 업데이트 단원을 참조하십시오.

Node.js
'use strict'; const content = ` <\!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Simple Lambda@Edge Static Content Response</title> </head> <body> <p>Hello from Lambda@Edge!</p> </body> </html> `; exports.handler = (event, context, callback) => { /* * Generate HTTP OK response using 200 status code with HTML body. */ const response = { status: '200', statusDescription: 'OK', headers: { 'cache-control': [{ key: 'Cache-Control', value: 'max-age=100' }], 'content-type': [{ key: 'Content-Type', value: 'text/html' }] }, body: content, }; callback(null, response); };
Python
import json CONTENT = """ <\!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Simple Lambda@Edge Static Content Response</title> </head> <body> <p>Hello from Lambda@Edge!</p> </body> </html> """ def lambda_handler(event, context): # Generate HTTP OK response using 200 status code with HTML body. response = { 'status': '200', 'statusDescription': 'OK', 'headers': { 'cache-control': [ { 'key': 'Cache-Control', 'value': 'max-age=100' } ], "content-type": [ { 'key': 'Content-Type', 'value': 'text/html' } ] }, 'body': CONTENT } return response

예제: HTTP 리디렉션 생성(생성된 응답)

다음 예제는 HTTP 리디렉션을 만드는 방법을 보여줍니다.

참고

최종 사용자 요청 및 오리진 요청 이벤트에 대해 HTTP 응답을 만들 수 있습니다. 자세한 내용은 요청 트리거에서 HTTP 응답 생성 단원을 참조하십시오.

Node.js
'use strict'; exports.handler = (event, context, callback) => { /* * Generate HTTP redirect response with 302 status code and Location header. */ const response = { status: '302', statusDescription: 'Found', headers: { location: [{ key: 'Location', value: 'http://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html', }], }, }; callback(null, response); };
Python
def lambda_handler(event, context): # Generate HTTP redirect response with 302 status code and Location header. response = { 'status': '302', 'statusDescription': 'Found', 'headers': { 'location': [{ 'key': 'Location', 'value': 'http://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html' }] } } return response

쿼리 문자열 처리 - 예제

이 섹션의 예제에는 Lambda@Edge를 쿼리 문자열과 함께 사용할 수 있는 방법이 포함되어 있습니다.

예: 쿼리 문자열 파라미터를 기반으로 헤더 추가

다음 예제에서는 쿼리 문자열 파라미터의 키-값 페어를 가져오고 그 값을 토대로 헤더를 추가하는 방법을 보여줍니다.

Node.js
'use strict'; const querystring = require('querystring'); exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; /* When a request contains a query string key-value pair but the origin server * expects the value in a header, you can use this Lambda function to * convert the key-value pair to a header. Here's what the function does: * 1. Parses the query string and gets the key-value pair. * 2. Adds a header to the request using the key-value pair that the function got in step 1. */ /* Parse request querystring to get javascript object */ const params = querystring.parse(request.querystring); /* Move auth param from querystring to headers */ const headerName = 'Auth-Header'; request.headers[headerName.toLowerCase()] = [{ key: headerName, value: params.auth }]; delete params.auth; /* Update request querystring */ request.querystring = querystring.stringify(params); callback(null, request); } ;
Python
from urllib.parse import parse_qs, urlencode def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] ''' When a request contains a query string key-value pair but the origin server expects the value in a header, you can use this Lambda function to convert the key-value pair to a header. Here's what the function does: 1. Parses the query string and gets the key-value pair. 2. Adds a header to the request using the key-value pair that the function got in step 1. ''' # Parse request querystring to get dictionary/json params = {k : v[0] for k, v in parse_qs(request['querystring']).items()} # Move auth param from querystring to headers headerName = 'Auth-Header' request['headers'][headerName.lower()] = [{'key': headerName, 'value': params['auth']}] del params['auth'] # Update request querystring request['querystring'] = urlencode(params) return request

예: 쿼리 문자열 파라미터를 정규화하여 캐시 적중률 향상

다음 예제에서는 CloudFront가 요청을 오리진에 전달하기 전에 쿼리 문자열을 다음과 같이 변경하여 캐시 적중률을 향상하는 방법을 보여 줍니다.

  • 파라미터 이름을 기준으로 키-값 페어를 알파벳순으로 정렬

  • 키-값 페어의 대/소문자를 소문자로 변경

자세한 내용은 쿼리 문자열 파라미터 기반의 콘텐츠 캐싱 단원을 참조하십시오.

Node.js
'use strict'; const querystring = require('querystring'); exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; /* When you configure a distribution to forward query strings to the origin and * to cache based on a whitelist of query string parameters, we recommend * the following to improve the cache-hit ratio: * - Always list parameters in the same order. * - Use the same case for parameter names and values. * * This function normalizes query strings so that parameter names and values * are lowercase and parameter names are in alphabetical order. * * For more information, see: * http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/QueryStringParameters.html */ console.log('Query String: ', request.querystring); /* Parse request query string to get javascript object */ const params = querystring.parse(request.querystring.toLowerCase()); const sortedParams = {}; /* Sort param keys */ Object.keys(params).sort().forEach(key => { sortedParams[key] = params[key]; }); /* Update request querystring with normalized */ request.querystring = querystring.stringify(sortedParams); callback(null, request); };
Python
from urllib.parse import parse_qs, urlencode def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] ''' When you configure a distribution to forward query strings to the origin and to cache based on a whitelist of query string parameters, we recommend the following to improve the cache-hit ratio: Always list parameters in the same order. - Use the same case for parameter names and values. This function normalizes query strings so that parameter names and values are lowercase and parameter names are in alphabetical order. For more information, see: http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/QueryStringParameters.html ''' print("Query string: ", request["querystring"]) # Parse request query string to get js object params = {k : v[0] for k, v in parse_qs(request['querystring'].lower()).items()} # Sort param keys sortedParams = sorted(params.items(), key=lambda x: x[0]) # Update request querystring with normalized request['querystring'] = urlencode(sortedParams) return request

예: 인증되지 않은 사용자를 로그인 페이지로 리디렉션

다음 예제에서는 사용자가 자격 증명을 입력하지 않은 경우 사용자를 로그인 페이지로 리디렉션하는 방법을 보여 줍니다.

Node.js
'use strict'; function parseCookies(headers) { const parsedCookie = {}; if (headers.cookie) { headers.cookie[0].value.split(';').forEach((cookie) => { if (cookie) { const parts = cookie.split('='); parsedCookie[parts[0].trim()] = parts[1].trim(); } }); } return parsedCookie; } exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const headers = request.headers; /* Check for session-id in request cookie in viewer-request event, * if session-id is absent, redirect the user to sign in page with original * request sent as redirect_url in query params. */ /* Check for session-id in cookie, if present then proceed with request */ const parsedCookies = parseCookies(headers); if (parsedCookies && parsedCookies['session-id']) { callback(null, request); return; } /* URI encode the original request to be sent as redirect_url in query params */ const encodedRedirectUrl = encodeURIComponent(`https://${headers.host[0].value}${request.uri}?${request.querystring}`); const response = { status: '302', statusDescription: 'Found', headers: { location: [{ key: 'Location', value: `https://www.example.com/signin?redirect_url=${encodedRedirectUrl}`, }], }, }; callback(null, response); };
Python
import urllib def parseCookies(headers): parsedCookie = {} if headers.get('cookie'): for cookie in headers['cookie'][0]['value'].split(';'): if cookie: parts = cookie.split('=') parsedCookie[parts[0].strip()] = parts[1].strip() return parsedCookie def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] headers = request['headers'] ''' Check for session-id in request cookie in viewer-request event, if session-id is absent, redirect the user to sign in page with original request sent as redirect_url in query params. ''' # Check for session-id in cookie, if present, then proceed with request parsedCookies = parseCookies(headers) if parsedCookies and parsedCookies['session-id']: return request # URI encode the original request to be sent as redirect_url in query params redirectUrl = "https://%s%s?%s" % (headers['host'][0]['value'], request['uri'], request['querystring']) encodedRedirectUrl = urllib.parse.quote_plus(redirectUrl.encode('utf-8')) response = { 'status': '302', 'statusDescription': 'Found', 'headers': { 'location': [{ 'key': 'Location', 'value': 'https://www.example.com/signin?redirect_url=%s' % encodedRedirectUrl }] } } return response

국가 또는 디바이스 유형 헤더에 따른 콘텐츠 개인 설정 - 예제

이 섹션의 예제는 Lambda@Edge를 사용하여 뷰어가 사용하는 위치나 장치 유형에 따라 동작을 사용자 정의하는 방법을 보여줍니다.

예: 최종 사용자 요청을 국가별 URL로 리디렉션

다음 예제에서는 국가별 URL이 포함된 HTTP 리디렉션 응답을 생성하고 해당 응답을 최종 사용자에게 반환하는 방법을 보여 줍니다. 이 작업은 국가별 응답을 제공하려는 경우에 유용합니다. 예:

  • 국가별 하위 도메인이 있는 경우(예: us.example.com 및 tw.example.com) 최종 사용자가 example.com을 요청할 때 리디렉션 응답을 생성할 수 있습니다.

  • 비디오를 스트리밍하지만 특정 국가에서 콘텐츠를 스트리밍할 권한이 없는 경우 비디오를 볼 수 없는 이유를 설명하는 페이지로 해당 국가의 사용자를 리디렉션할 수 있습니다.

다음을 참조하십시오.

  • CloudFront-Viewer-Country 헤더 값을 기반으로 캐시하도록 배포를 구성해야 합니다. 자세한 내용은 선택한 요청 헤더 기반의 캐시 단원을 참조하십시오.

  • CloudFront는 최종 사용자 요청 이벤트 뒤에 CloudFront-Viewer-Country 헤더를 추가합니다. 이 예제를 사용하려면 오리진 요청 이벤트에 대한 트리거를 생성해야 합니다.

Node.js
'use strict'; /* This is an origin request function */ exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const headers = request.headers; /* * Based on the value of the CloudFront-Viewer-Country header, generate an * HTTP status code 302 (Redirect) response, and return a country-specific * URL in the Location header. * NOTE: 1. You must configure your distribution to cache based on the * CloudFront-Viewer-Country header. For more information, see * http://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers * 2. CloudFront adds the CloudFront-Viewer-Country header after the viewer * request event. To use this example, you must create a trigger for the * origin request event. */ let url = 'https://example.com/'; if (headers['cloudfront-viewer-country']) { const countryCode = headers['cloudfront-viewer-country'][0].value; if (countryCode === 'TW') { url = 'https://tw.example.com/'; } else if (countryCode === 'US') { url = 'https://us.example.com/'; } } const response = { status: '302', statusDescription: 'Found', headers: { location: [{ key: 'Location', value: url, }], }, }; callback(null, response); };
Python
# This is an origin request function def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] headers = request['headers'] ''' Based on the value of the CloudFront-Viewer-Country header, generate an HTTP status code 302 (Redirect) response, and return a country-specific URL in the Location header. NOTE: 1. You must configure your distribution to cache based on the CloudFront-Viewer-Country header. For more information, see http://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers 2. CloudFront adds the CloudFront-Viewer-Country header after the viewer request event. To use this example, you must create a trigger for the origin request event. ''' url = 'https://example.com/' viewerCountry = headers.get('cloudfront-viewer-country') if viewerCountry: countryCode = viewerCountry[0]['value'] if countryCode == 'TW': url = 'https://tw.example.com/' elif countryCode == 'US': url = 'https://us.example.com/' response = { 'status': '302', 'statusDescription': 'Found', 'headers': { 'location': [{ 'key': 'Location', 'value': url }] } } return response

예: 디바이스를 기반으로 객체의 다양한 버전 서비스

다음 예제에서는 사용자가 사용 중인 디바이스 유형(예: 모바일 디바이스 또는 태블릿)을 기반으로 객체의 다양한 버전을 서비스하는 방법을 보여 줍니다. 다음을 참조하십시오.

  • CloudFront-Is-*-Viewer 헤더 값을 기반으로 캐시하도록 배포를 구성해야 합니다. 자세한 내용은 선택한 요청 헤더 기반의 캐시 단원을 참조하십시오.

  • CloudFront는 최종 사용자 요청 이벤트 뒤에 CloudFront-Is-*-Viewer 헤더를 추가합니다. 이 예제를 사용하려면 오리진 요청 이벤트에 대한 트리거를 생성해야 합니다.

Node.js
'use strict'; /* This is an origin request function */ exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const headers = request.headers; /* * Serve different versions of an object based on the device type. * NOTE: 1. You must configure your distribution to cache based on the * CloudFront-Is-*-Viewer headers. For more information, see * the following documentation: * http://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers * http://docs.aws.amazon.com/console/cloudfront/cache-on-device-type * 2. CloudFront adds the CloudFront-Is-*-Viewer headers after the viewer * request event. To use this example, you must create a trigger for the * origin request event. */ const desktopPath = '/desktop'; const mobilePath = '/mobile'; const tabletPath = '/tablet'; const smarttvPath = '/smarttv'; if (headers['cloudfront-is-desktop-viewer'] && headers['cloudfront-is-desktop-viewer'][0].value === 'true') { request.uri = desktopPath + request.uri; } else if (headers['cloudfront-is-mobile-viewer'] && headers['cloudfront-is-mobile-viewer'][0].value === 'true') { request.uri = mobilePath + request.uri; } else if (headers['cloudfront-is-tablet-viewer'] && headers['cloudfront-is-tablet-viewer'][0].value === 'true') { request.uri = tabletPath + request.uri; } else if (headers['cloudfront-is-smarttv-viewer'] && headers['cloudfront-is-smarttv-viewer'][0].value === 'true') { request.uri = smarttvPath + request.uri; } console.log(`Request uri set to "${request.uri}"`); callback(null, request); };
Python
# This is an origin request function def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] headers = request['headers'] ''' Serve different versions of an object based on the device type. NOTE: 1. You must configure your distribution to cache based on the CloudFront-Is-*-Viewer headers. For more information, see the following documentation: http://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers http://docs.aws.amazon.com/console/cloudfront/cache-on-device-type 2. CloudFront adds the CloudFront-Is-*-Viewer headers after the viewer request event. To use this example, you must create a trigger for the origin request event. ''' desktopPath = '/desktop'; mobilePath = '/mobile'; tabletPath = '/tablet'; smarttvPath = '/smarttv'; if 'cloudfront-is-desktop-viewer' in headers and headers['cloudfront-is-desktop-viewer'][0]['value'] == 'true': request['uri'] = desktopPath + request['uri'] elif 'cloudfront-is-mobile-viewer' in headers and headers['cloudfront-is-mobile-viewer'][0]['value'] == 'true': request['uri'] = mobilePath + request['uri'] elif 'cloudfront-is-tablet-viewer' in headers and headers['cloudfront-is-tablet-viewer'][0]['value'] == 'true': request['uri'] = tabletPath + request['uri'] elif 'cloudfront-is-smarttv-viewer' in headers and headers['cloudfront-is-smarttv-viewer'][0]['value'] == 'true': request['uri'] = smarttvPath + request['uri'] print("Request uri set to %s" % request['uri']) return request

콘텐츠 기반 동적 오리진 선택 - 예제

이 섹션의 예제는 Lambda@Edge를 사용하여 요청 정보를 기반으로 다른 오리진으로 라우팅하는 방법을 보여줍니다.

예제: 오리진 요청 트리거를 사용하여 사용자 지정 오리진에서 Amazon S3 오리진으로 변경

이 함수는 오리진 요청 트리거를 사용하여 사용자 지정 오리진에서 콘텐츠를 요청 속성을 기반으로 가져오는 Amazon S3 오리진으로 변경하는 방법을 보여줍니다.

Node.js
'use strict'; const querystring = require('querystring'); exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; /** * Reads query string to check if S3 origin should be used, and * if true, sets S3 origin properties. */ const params = querystring.parse(request.querystring); if (params['useS3Origin']) { if (params['useS3Origin'] === 'true') { const s3DomainName = 'my-bucket.s3.amazonaws.com'; /* Set S3 origin fields */ request.origin = { s3: { domainName: s3DomainName, region: '', authMethod: 'none', path: '', customHeaders: {} } }; request.headers['host'] = [{ key: 'host', value: s3DomainName}]; } } callback(null, request); };
Python
from urllib.parse import parse_qs def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] ''' Reads query string to check if S3 origin should be used, and if true, sets S3 origin properties ''' params = {k: v[0] for k, v in parse_qs(request['querystring']).items()} if params.get('useS3Origin') == 'true': s3DomainName = 'my-bucket.s3.amazonaws.com' # Set S3 origin fields request['origin'] = { 's3': { 'domainName': s3DomainName, 'region': '', 'authMethod': 'none', 'path': '', 'customHeaders': {} } } request['headers']['host'] = [{'key': 'host', 'value': s3DomainName}] return request

예제: 오리진 요청 트리거를 사용하여 Amazon S3 오리진 리전 변경

이 함수는 오리진 요청 트리거를 사용하여 콘텐츠를 요청 속성을 기반으로 가져오는 Amazon S3 오리진을 변경하는 방법을 보여줍니다.

이 예제에서는 CloudFront-Viewer-Country 헤더의 값을 사용하여 S3 버킷 도메인 이름을 최종 사용자와 인접한 리전에 있는 버킷으로 업데이트합니다. 이는 여러 방법에서 유용할 수 있습니다.

  • 지정된 리전이 최종 사용자의 국가와 더욱 근접할 때 지연 시간을 감소합니다.

  • 데이터가 요청이 온 곳과 동일한 국가에 있는 리전에서 서비스되는지 확인함으로써 데이터 주권을 제공합니다.

이 예제를 활용하려면 다음과 같이 해야 합니다.

  • CloudFront-Viewer-Country 헤더를 기반으로 캐시하도록 배포를 구성합니다. 자세한 내용은 선택한 요청 헤더 기반의 캐시 단원을 참조하십시오.

  • 오리진 요청 이벤트에서 이 함수에 대한 트리거를 생성합니다. CloudFront는 최종 사용자 요청 이벤트 후에 CloudFront-Viewer-Country 헤더를 추가합니다. 이 예제를 사용하려면 오리진 요청에서 함수가 실행되는지 확인해야 합니다.

Node.js
'use strict'; exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; /** * This blueprint demonstrates how an origin-request trigger can be used to * change the origin from which the content is fetched, based on request properties. * In this example, we use the value of the CloudFront-Viewer-Country header * to update the S3 bucket domain name to a bucket in a Region that is closer to * the viewer. * * This can be useful in several ways: * 1) Reduces latencies when the Region specified is nearer to the viewer’s * country. * 2) Provides data sovereignty by making sure that data is served from an * origin that’s in the same country that the request came from. * * NOTE: 1. You must configure your distribution to cache based on the * CloudFront-Viewer-Country header. For more information, see * http://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers * 2. CloudFront adds the CloudFront-Viewer-Country header after the viewer * request event. To use this example, you must create a trigger for the * origin request event. */ const countryToRegion = { 'DE': 'eu-central-1', 'IE': 'eu-west-1', 'GB': 'eu-west-2', 'FR': 'eu-west-3', 'JP': 'ap-northeast-1', 'IN': 'ap-south-1' }; if (request.headers['cloudfront-viewer-country']) { const countryCode = request.headers['cloudfront-viewer-country'][0].value; const region = countryToRegion[countryCode]; /** * If the viewer's country is not in the list you specify, the request * goes to the default S3 bucket you've configured. */ if (region) { /** * If you’ve set up OAI, the bucket policy in the destination bucket * should allow the OAI GetObject operation, as configured by default * for an S3 origin with OAI. Another requirement with OAI is to provide * the Region so it can be used for the SIGV4 signature. Otherwise, the * Region is not required. */ request.origin.s3.region = region; const domainName = `my-bucket-in-${region}.s3.amazonaws.com`; request.origin.s3.domainName = domainName; request.headers['host'] = [{ key: 'host', value: domainName }]; } } callback(null, request); };
Python
def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] ''' This blueprint demonstrates how an origin-request trigger can be used to change the origin from which the content is fetched, based on request properties. In this example, we use the value of the CloudFront-Viewer-Country header to update the S3 bucket domain name to a bucket in a Region that is closer to the viewer. This can be useful in several ways: 1) Reduces latencies when the Region specified is nearer to the viewer’s country. 2) Provides data sovereignty by making sure that data is served from an origin that’s in the same country that the request came from. NOTE: 1. You must configure your distribution to cache based on the CloudFront-Viewer-Country header. For more information, see http://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers 2. CloudFront adds the CloudFront-Viewer-Country header after the viewer request event. To use this example, you must create a trigger for the origin request event. ''' countryToRegion = { 'DE': 'eu-central-1', 'IE': 'eu-west-1', 'GB': 'eu-west-2', 'FR': 'eu-west-3', 'JP': 'ap-northeast-1', 'IN': 'ap-south-1' } viewerCountry = request['headers'].get('cloudfront-viewer-country') if viewerCountry: countryCode = viewerCountry[0]['value'] region = countryToRegion.get(countryCode) # If the viewer's country in not in the list you specify, the request # goes to the default S3 bucket you've configured if region: ''' If you’ve set up OAI, the bucket policy in the destination bucket should allow the OAI GetObject operation, as configured by default for an S3 origin with OAI. Another requirement with OAI is to provide the Region so it can be used for the SIGV4 signature. Otherwise, the Region is not required. ''' request['origin']['s3']['region'] = region domainName = 'my-bucket-in-%s.s3.amazonaws.com' % region request['origin']['s3']['domainName'] = domainName request['headers']['host'] = [{'key': 'host', 'value': domainName}] return request

예제: 오리진 요청 트리거를 사용하여 Amazon S3 오리진에서 사용자 지정 오리진으로 변경

이 함수는 오리진 요청 트리거를 사용하여 콘텐츠를 요청 속성을 기반으로 가져오는 사용자 지정 오리진을 변경하는 방법을 보여줍니다.

Node.js
'use strict'; const querystring = require('querystring'); exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; /** * Reads query string to check if custom origin should be used, and * if true, sets custom origin properties. */ const params = querystring.parse(request.querystring); if (params['useCustomOrigin']) { if (params['useCustomOrigin'] === 'true') { /* Set custom origin fields*/ request.origin = { custom: { domainName: 'www.example.com', port: 443, protocol: 'https', path: '', sslProtocols: ['TLSv1', 'TLSv1.1'], readTimeout: 5, keepaliveTimeout: 5, customHeaders: {} } }; request.headers['host'] = [{ key: 'host', value: 'www.example.com'}]; } } callback(null, request); };
Python
from urllib.parse import parse_qs def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] # Reads query string to check if custom origin should be used, and # if true, sets custom origin properties params = {k: v[0] for k, v in parse_qs(request['querystring']).items()} if params.get('useCustomOrigin') == 'true': # Set custom origin fields request['origin'] = { 'custom': { 'domainName': 'www.example.com', 'port': 443, 'protocol': 'https', 'path': '', 'sslProtocols': ['TLSv1', 'TLSv1.1'], 'readTimeout': 5, 'keepaliveTimeout': 5, 'customHeaders': {} } } request['headers']['host'] = [{'key': 'host', 'value': 'www.example.com'}] return request

예제: 오리진 요청 트리거를 사용하여 하나의 Amazon S3 버킷에서 다른 버킷으로 점진적으로 트래픽 전송

이 함수는 통제된 방법을 통해 하나의 Amazon S3 버킷에서 다른 버킷으로 점진적으로 트래픽을 전송하는 방법을 보여줍니다.

Node.js
'use strict'; function getRandomInt(min, max) { /* Random number is inclusive of min and max*/ return Math.floor(Math.random() * (max - min + 1)) + min; } exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const BLUE_TRAFFIC_PERCENTAGE = 80; /** * This Lambda function demonstrates how to gradually transfer traffic from * one S3 bucket to another in a controlled way. * We define a variable BLUE_TRAFFIC_PERCENTAGE which can take values from * 1 to 100. If the generated randomNumber less than or equal to BLUE_TRAFFIC_PERCENTAGE, traffic * is re-directed to blue-bucket. If not, the default bucket that we've configured * is used. */ const randomNumber = getRandomInt(1, 100); if (randomNumber <= BLUE_TRAFFIC_PERCENTAGE) { const domainName = 'blue-bucket.s3.amazonaws.com'; request.origin.s3.domainName = domainName; request.headers['host'] = [{ key: 'host', value: domainName}]; } callback(null, request); };
Python
import math import random def getRandomInt(min, max): # Random number is inclusive of min and max return math.floor(random.random() * (max - min + 1)) + min def lambda_handler(min, max): request = event['Records'][0]['cf']['request'] BLUE_TRAFFIC_PERCENTAGE = 80 ''' This Lambda function demonstrates how to gradually transfer traffic from one S3 bucket to another in a controlled way. We define a variable BLUE_TRAFFIC_PERCENTAGE which can take values from 1 to 100. If the generated randomNumber less than or equal to BLUE_TRAFFIC_PERCENTAGE, traffic is re-directed to blue-bucket. If not, the default bucket that we've configured is used. ''' randomNumber = getRandomInt(1, 100) if randomNumber <= BLUE_TRAFFIC_PERCENTAGE: domainName = 'blue-bucket.s3.amazonaws.com' request['origin']['s3']['domainName'] = domainName request['heaaders']['host'] = [{'key': 'host', 'value': domainName}] return request

예제: 오리진 요청 트리거를 사용하여 국가 헤더를 기반으로 한 오리진 도메인 이름 변경

이 함수는 콘텐츠가 최종 사용자의 국가에서 가까운 오리진에서 서비스되도록 CloudFront-Viewer-Country 헤더를 기반으로 오리진 도메인 이름을 변경하는 방법을 보여줍니다.

배포에서 이 기능을 구현하면 다음과 같은 이점을 누릴 수 있습니다.

  • 지정된 리전이 최종 사용자의 국가와 더욱 근접할 때 지연 시간이 감소

  • 데이터가 요청이 온 곳과 동일한 국가에 있는 오리진에서 서비스되는지 확인함으로써 데이터 주권을 제공

이 기능을 활성화하려면 CloudFront-Viewer-Country 헤더를 기반으로 캐시할 배포를 구성해야 합니다. 자세한 내용은 선택한 요청 헤더 기반의 캐시 단원을 참조하십시오.

Node.js
'use strict'; exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; if (request.headers['cloudfront-viewer-country']) { const countryCode = request.headers['cloudfront-viewer-country'][0].value; if (countryCode === 'GB' || countryCode === 'DE' || countryCode === 'IE' ) { const domainName = 'eu.example.com'; request.origin.custom.domainName = domainName; request.headers['host'] = [{key: 'host', value: domainName}]; } } callback(null, request); };
Python
def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] viewerCountry = request['headers'].get('cloudfront-viewer-country') if viewerCountry: countryCode = viewerCountry[0]['value'] if countryCode == 'GB' or countryCode == 'DE' or countryCode == 'IE': domainName = 'eu.example.com' request['origin']['custom']['domainName'] = domainName request['headers']['host'] = [{'key': 'host', 'value': domainName}] return request

오류 상태 업데이트 - 예제

이 섹션의 예제는 Lambda@Edge를 사용하여 사용자에게 반환되는 오류의 상태를 변경하는 방법에 대한 지침을 제공합니다.

예제: 오리진 응답 트리거를 사용하여 오류 상태 코드를 200-OK로 업데이트

이 함수는 응답 상태를 200으로 업데이트하고 정적 본문 콘텐츠를 생성하여 다음 시나리오에서 최종 사용자에게 반환하는 방법을 보여줍니다.

  • 함수가 오리진 응답에서 트리거됩니다.

  • 오리진 서버의 응답 상태는 오류 상태 코드(4xx 또는 5xx)입니다.

Node.js
'use strict'; exports.handler = (event, context, callback) => { const response = event.Records[0].cf.response; /** * This function updates the response status to 200 and generates static * body content to return to the viewer in the following scenario: * 1. The function is triggered in an origin response * 2. The response status from the origin server is an error status code (4xx or 5xx) */ if (response.status >= 400 && response.status <= 599) { response.status = 200; response.statusDescription = 'OK'; response.body = 'Body generation example'; } callback(null, response); };
Python
def lambda_handler(event, context): response = event['Records'][0]['cf']['response'] ''' This function updates the response status to 200 and generates static body content to return to the viewer in the following scenario: 1. The function is triggered in an origin response 2. The response status from the origin server is an error status code (4xx or 5xx) ''' if int(response['status']) >= 400 and int(response['status']) <= 599: response['status'] = 200 response['statusDescription'] = 'OK' response['body'] = 'Body generation example' return response

예제: 오리진 응답 트리거를 사용하여 오류 상태 코드를 302-Found로 업데이트

이 함수는 HTTP 상태 코드를 302로 업데이트하여 다른 오리진이 구성된 다른 경로(캐시 동작)로 리디렉션하는 방법을 보여줍니다. 다음을 참조하십시오.

  • 함수가 오리진 응답에서 트리거됩니다.

  • 오리진 서버의 응답 상태는 오류 상태 코드(4xx 또는 5xx)입니다.

Node.js
'use strict'; exports.handler = (event, context, callback) => { const response = event.Records[0].cf.response; const request = event.Records[0].cf.request; /** * This function updates the HTTP status code in the response to 302, to redirect to another * path (cache behavior) that has a different origin configured. Note the following: * 1. The function is triggered in an origin response * 2. The response status from the origin server is an error status code (4xx or 5xx) */ if (response.status >= 400 && response.status <= 599) { const redirect_path = `/plan-b/path?${request.querystring}`; response.status = 302; response.statusDescription = 'Found'; /* Drop the body, as it is not required for redirects */ response.body = ''; response.headers['location'] = [{ key: 'Location', value: redirect_path }]; } callback(null, response); };
Python
def lambda_handler(event, context): response = event['Records'][0]['cf']['response'] request = event['Records'][0]['cf']['request'] ''' This function updates the HTTP status code in the response to 302, to redirect to another path (cache behavior) that has a different origin configured. Note the following: 1. The function is triggered in an origin response 2. The response status from the origin server is an error status code (4xx or 5xx) ''' if int(response['status']) >= 400 and int(response['status']) <= 599: redirect_path = '/plan-b/path?%s' % request['querystring'] response['status'] = 302 response['statusDescription'] = 'Found' # Drop the body as it is not required for redirects response['body'] = '' response['headers']['location'] = [{'key': 'Location', 'value': redirect_path}] return response

요청 본문에 액세스 - 예

이 섹션의 예제는 Lambda@Edge를 사용하여 POST 요청을 처리하는 방법을 보여줍니다.

참고

이러한 예제를 사용하려면 배포의 Lambda 함수 연결에서 본문 포함 옵션을 활성화해야 합니다. 기본적으로 활성화되어 있지 않습니다.

  • CloudFront 콘솔에서 이 설정을 활성화하려면 Lambda Function Association(Lambda 함수 연결)에서 본문 포함 확인란을 선택하십시오.

  • CloudFront API 또는 AWS CloudFormation에서 이 설정을 활성화하려면 LambdaFunctionAssociation에서 IncludeBody 필드를 true로 설정하십시오.

예: 요청 트리거를 사용하여 HTML 양식 읽기

이 기능은 "문의처" 양식 등과 같은 HTML 양식(웹 양식)으로 인해 생성된 POST 요청의 본문을 처리할 수 있는 방법을 보여줍니다. 예를 들어, 다음과 같은 HTML 양식이 있을 수 있습니다.

<html> <form action="http://example.com" method="post"> Param 1: <input type="text" name="name1"><br> Param 2: <input type="text" name="name2"><br> input type="submit" value="Submit"> </form> </html>

예를 들어, 다음 함수는 CloudFront 최종 사용자 요청 또는 오리진 요청 시 실행되어야 합니다.

Node.js
'use strict'; const querystring = require('querystring'); /** * This function demonstrates how you can read the body of a POST request * generated by an HTML form (web form). The function is triggered in a * CloudFront viewer request or origin request event type. */ exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; if (request.method === 'POST') { /* HTTP body is always passed as base64-encoded string. Decode it. */ const body = Buffer.from(request.body.data, 'base64').toString(); /* HTML forms send the data in query string format. Parse it. */ const params = querystring.parse(body); /* For demonstration purposes, we only log the form fields here. * You can put your custom logic here. For example, you can store the * fields in a database, such as AWS DynamoDB, and generate a response * right from your Lambda@Edge function. */ for (let param in params) { console.log(`For "${param}" user submitted "${params[param]}".\n`); } } return callback(null, request); };
Python
import base64 from urllib.parse import parse_qs ''' Say there is a POST request body generated by an HTML such as: <html> <form action="http://example.com" method="post"> Param 1: <input type="text" name="name1"><br> Param 2: <input type="text" name="name2"><br> input type="submit" value="Submit"> </form> </html> ''' ''' This function demonstrates how you can read the body of a POST request generated by an HTML form (web form). The function is triggered in a CloudFront viewer request or origin request event type. ''' def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] if request['method'] == 'POST': # HTTP body is always passed as base64-encoded string. Decode it body = base64.b64decode(request['body']['data']) # HTML forms send the data in query string format. Parse it params = {k: v[0] for k, v in parse_qs(body).items()} ''' For demonstration purposes, we only log the form fields here. You can put your custom logic here. For example, you can store the fields in a database, such as AWS DynamoDB, and generate a response right from your Lambda@Edge function. ''' for key, value in params.items(): print("For %s use submitted %s" % (key, value)) return request

예: 요청 트리거를 사용하여 HTML 양식 수정

이 기능은 "문의처" 양식 등과 같은 HTML 양식(웹 양식)으로 인해 생성된 POST 요청의 본문을 수정할 수 있는 방법을 보여줍니다. 이 함수는 CloudFront 최종 사용자 요청 또는 오리진 요청 시 실행됩니다.

Node.js
'use strict'; const querystring = require('querystring'); exports.handler = (event, context, callback) => { var request = event.Records[0].cf.request; if (request.method === 'POST') { /* Request body is being replaced. To do this, update the following /* three fields: * 1) body.action to 'replace' * 2) body.encoding to the encoding of the new data. * * Set to one of the following values: * * text - denotes that the generated body is in text format. * Lambda@Edge will propagate this as is. * base64 - denotes that the generated body is base64 encoded. * Lambda@Edge will base64 decode the data before sending * it to the origin. * 3) body.data to the new body. */ request.body.action = 'replace'; request.body.encoding = 'text'; request.body.data = getUpdatedBody(request); } callback(null, request); }; function getUpdatedBody(request) { /* HTTP body is always passed as base64-encoded string. Decode it. */ const body = Buffer.from(request.body.data, 'base64').toString(); /* HTML forms send data in query string format. Parse it. */ const params = querystring.parse(body); /* For demonstration purposes, we're adding one more param. * * You can put your custom logic here. For example, you can truncate long * bodies from malicious requests. */ params['new-param-name'] = 'new-param-value'; return querystring.stringify(params); }
Python
import base64 from urllib.parse import parse_qs, urlencode def lambda_handler(event, context): request = event['Records'][0]['cf']['request'] if request['method'] == 'POST': ''' Request body is being replaced. To do this, update the following three fields: 1) body.action to 'replace' 2) body.encoding to the encoding of the new data. Set to one of the following values: text - denotes that the generated body is in text format. Lambda@Edge will propagate this as is. base64 - denotes that the generated body is base64 encoded. Lambda@Edge will base64 decode the data before sending it to the origin. 3) body.data to the new body. ''' request['body']['action'] = 'replace' request['body']['encoding'] = 'text' request['body']['data'] = getUpdatedBody(request) return request def getUpdatedBody(request): # HTTP body is always passed as base64-encoded string. Decode it body = base64.b64decode(request['body']['data']) # HTML forms send data in query string format. Parse it params = {k: v[0] for k, v in parse_qs(body).items()} # For demonstration purposes, we're adding one more param # You can put your custom logic here. For example, you can truncate long # bodies from malicious requests params['new-param-name'] = 'new-param-value' return urlencode(params)