Giter VIP home page Giter VIP logo

serverless-offline-edge-lambda's Introduction

serverless-offline-edge-lambda

A plugin for the Serverless Framework that simulates the behavior of AWS CloudFront Edge Lambdas while developing offline.

Setup

npm install --save-dev serverless
npm install --save-dev serverless-offline-edge-lambda

serverless.yml

service:
  name: edge-lambdas

plugins:
  - serverless-offline-edge-lambda

provider:
  name: aws
  runtime: nodejs12.x

functions:
  lambda:
    handler: src/handlers.onViewerRequest
    lambdaAtEdge:
      distribution: 'WebsiteDistribution'
      eventType: 'viewer-request'
      pathPattern: '/lambda'

resources:
  Resources:
    WebsiteDistribution:
      Type: 'AWS::CloudFront::Distribution'
      Properties:
        DistributionConfig:
          DefaultCacheBehavior:
npx serverless offline start --port=<port>

Use with serverless-offline

The plugin should not be used in conjunction with serverless-offline because both plugins define the offline command.

Use with serverless-plugin-cloudfront-lambda-edge

This plugin does not handle packaging and deploying edge lambdas to the cloud. Therefore this plugin can be used with serverless-plugin-cloudfront-lambda-edge. Again, doing so is optional. The schema in serverless.yml derives from that used by serverless-plugin-cloudfront-lambda-edge.

Use with Transpilers

This plugin can also be used with transpilers such as serverless-plugin-typescript. In the cases where the transpiler outputs built files to a path that differs from the path specified for the handlers (e.g. .build/src/handers.onViewerRequest), this plugin accepts a configuration option path that it uses to resolve function handlers.

plugins:
  - serverless-plugin-typescript

custom:
  offlineEdgeLambda:
    path: '.build'

For usage with serverless-webpack and serverless-bundle the config is similar but the build path changes.

plugins:
  - serverless-webpack # or serverless-bundle

custom:
  offlineEdgeLambda:
    path: './.webpack/service/'

Hot Reload Support

Hot reload for serverless-esbuild and serverless-plugin-typescript are available with extra configuration.

The watch/reload mechanism is available form serverless-webpack, but is disabled by default for esbuild and typescript.

The flag "watchReload: true" will turn on the watcher so that typescript and esbuild solutions use the watcher to hot reload the handlers. The path to the built handlers must be specified for the watcher to work correctly.

example:

custom:
  offlineEdgeLambda:
    path: '.esbuild/service'
    watchReload: true

Additional options can be used to modify the behavior of the file watcher and debounce logic (ignoreInitial, awaitWriteFinish, interval, debounce, and any other chokidar option).

example:

custom:
  offlineEdgeLambda:
    path: '.dist/service'
    watchReload: true
    ignoreInitial: true
    awaitWriteFinish: true
    interval: 500,
    debounce: 750

Options

--headersFile

Default: undefined

CloudFront injects some headers into the request. You can set these by creating a JSON file and passing its path as an option.

Example:

// .cf-headers.json
{
    "CloudFront-Viewer-Country": "us",
    "CloudFront-Viewer-Country-Region": "tx"
}
npx serverless offline start --headersFile .cf-headers.json

serverless-offline-edge-lambda's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar dnicolson avatar matthewstrom avatar mattstrom avatar robblovell avatar ryanolee avatar semantic-release-bot avatar sprmn avatar tysonstewart avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

serverless-offline-edge-lambda's Issues

Support for custom headers

I have a Lambda@Edge associated with a resource of type AWS::CloudFront::Distribution. This cloud distribution configures a few custom headers that are part of every event sent through to the Lambda@Edge. This plugin seems to be ignoring those custom headers. Can this be supported? Below are relevant snippets from my serverless.yml:

functions:
  MyLambdaFunc:
    name: 'my-handkler-${self:provider.stage}'
    handler: my-handkler.originRequest"
    memorySize: 256
    timeout: 20
    lambdaAtEdge:
      distribution: 'CloudFrontDistribution'
      eventType: 'origin-request'

...


resources:
  Resources:
    CloudFrontDistribution:
      Type: AWS::CloudFront::Distribution
      Properties:
        DistributionConfig:
          Enabled: true
          DefaultCacheBehavior:
            TargetOriginId: 'S3-blah'
            ViewerProtocolPolicy: 'allow-all'
            CachePolicyId:
              Fn::GetAtt: ['CloudFrontCachePolicy', 'Id']
            Compress: false
            ForwardedValues:
              QueryString: false
              Cookies:
                Forward: 'none'
          Origins:
            - DomainName: "blah.s3.amazonaws.com"
              OriginCustomHeaders:
                - HeaderName: 'header-one-name'
                  HeaderValue: 'header-one-val'
                - HeaderName: 'header-two-name'
                  HeaderValue: 'header-two-val'
...

Notice the OriginCustomHeaders: that should be part of every request event. AWS puts the custom headers in the request JSON as follows,

{
  Records: [
    {
      cf: {
        request: {
          origin: {
            s3: {
              customHeaders: {
                'header-one-name': [{ header-one-val' }],
                'header-two-name': [{ value: 'header-two-val' }]
              }
            }
          },
          headers: {
            ...
          }
        },
        response: {
          headers: {}
        }
      }
    }
  ]
}

Origin request hangs forever

I'm using serverless 2.11.1 and I'm having a hard time getting this plugin to work. In my origin request handler I am dynamically setting the origin but when I try it I get...

Serverless: Request for /    
Serverless: → viewer-request 
Serverless: → cache           
Serverless: ✗ Cache miss                                                                                                                 
Serverless: → origin-request
Serverless: → origin

And then the request just hangs forever. I've tried both https origins and S3 origins.

Getting `TypeError: Cannot use 'in' operator to search for 'status' in undefined` when my Lambda@Edge handler uses `callback(null, response)` to return response

When I use the Lambda@Edge handler callback(null, response) style of returning the response, I get the error below. It looks like the code is not aware of this style. See snippet farther below from src/services/cloudfront.service.ts. It's only passing event and context, but not a callback, return this.fnSet.originResponse(event, this.context);. Is this functionality that we'd want to add? If the answer is yes and I can carve out some time, I can take a look at submitting a PR for this issue. Thank you.

{"code":500,"message":"TypeError: Cannot use 'in' operator to search for 'status' in undefined\n    at Object.isResponseResult (/my-module/node_modules/serverless-offline-edge-lambda/dist/utils/is-response-result.js:5:21)\n    at CloudFrontLifecycle.<anonymous> (/my-module/node_modules/serverless-offline-edge-lambda/dist/services/cloudfront.service.js:100:25)\n    at Generator.next (<anonymous>)\n    at fulfilled (/my-module/node_modules/serverless-offline-edge-lambda/dist/services/cloudfront.service.js:5:58)\n    at processTicksAndRejections (internal/process/task_queues.js:95:5)"}* Closing connection 0
async onOriginResponse(result: CloudFrontResponseResult) {
		this.log('← origin-response');

		const event = combineResult(this.event, result);
		return this.fnSet.originResponse(event, this.context);
	}

Getting error `Serverless plugin "serverless-offline-edge-lambda" not found` after upgrading to 1.1.1

Looks like the tarball in the NPM registry wasn't built properly. Compare 1.0.1 and 1.1.1 versions. The latter only contains 3 files, the code is missing:

Version 1.1.1:
$ npm pack serverless-offline-edge-lambda
npm notice 
npm notice 📦  [email protected]
npm notice === Tarball Contents === 
npm notice 563B  LICENSE     
npm notice 1.9kB README.md   
npm notice 2.5kB package.json
npm notice === Tarball Details === 
npm notice name:          serverless-offline-edge-lambda          
npm notice version:       1.1.1                                   
npm notice filename:      serverless-offline-edge-lambda-1.1.1.tgz
npm notice package size:  2.1 kB                                  
npm notice unpacked size: 4.9 kB                                  
npm notice shasum:        e95710cac58af5eaa8d04e58cb8117bca45ee893
npm notice integrity:     sha512-y0k/atza6+dO/[...]nAduECoQ6RiMw==
npm notice total files:   3                                       
npm notice 
serverless-offline-edge-lambda-1.1.1.tgz
Version 1.0.1
$ npm pack [email protected]
npm notice 
npm notice 📦  [email protected]
npm notice === Tarball Contents === 
npm notice 563B  LICENSE                                           
npm notice 1.9kB README.md                                         
npm notice 889B  dist/behavior-router.d.ts                         
npm notice 9.5kB dist/behavior-router.js                           
npm notice 6.2kB dist/behavior-router.js.map                       
npm notice 39B   dist/constants.d.ts                               
npm notice 140B  dist/constants.js                                 
npm notice 141B  dist/constants.js.map                             
npm notice 35B   dist/errors/index.d.ts                            
npm notice 247B  dist/errors/index.js                              
npm notice 128B  dist/errors/index.js.map                          
npm notice 34B   dist/errors/no-result.error.d.ts                  
npm notice 168B  dist/errors/no-result.error.js                    
npm notice 166B  dist/errors/no-result.error.js.map                
npm notice 1.2kB dist/function-set.d.ts                            
npm notice 3.9kB dist/function-set.js                              
npm notice 2.2kB dist/function-set.js.map                          
npm notice 439B  dist/index.d.ts                                   
npm notice 3.4kB dist/index.js                                     
npm notice 1.6kB dist/index.js.map                                 
npm notice 426B  dist/middlewares/async.middleware.d.ts            
npm notice 260B  dist/middlewares/async.middleware.js              
npm notice 320B  dist/middlewares/async.middleware.js.map          
npm notice 132B  dist/middlewares/cloudfront-post.middleware.d.ts  
npm notice 775B  dist/middlewares/cloudfront-post.middleware.js    
npm notice 627B  dist/middlewares/cloudfront-post.middleware.js.map
npm notice 82B   dist/middlewares/index.d.ts                       
npm notice 299B  dist/middlewares/index.js                         
npm notice 146B  dist/middlewares/index.js.map                     
npm notice 0B    dist/polyfills.d.ts                               
npm notice 145B  dist/polyfills.js                                 
npm notice 214B  dist/polyfills.js.map                             
npm notice 334B  dist/services/cache.service.d.ts                  
npm notice 1.9kB dist/services/cache.service.js                    
npm notice 1.1kB dist/services/cache.service.js.map                
npm notice 1.1kB dist/services/cloudfront.service.d.ts             
npm notice 4.3kB dist/services/cloudfront.service.js               
npm notice 2.9kB dist/services/cloudfront.service.js.map           
npm notice 105B  dist/services/index.d.ts                          
npm notice 327B  dist/services/index.js                            
npm notice 156B  dist/services/index.js.map                        
npm notice 422B  dist/services/origin.service.d.ts                 
npm notice 5.1kB dist/services/origin.service.js                   
npm notice 3.4kB dist/services/origin.service.js.map               
npm notice 260B  dist/types/cloudfront.types.d.ts                  
npm notice 121B  dist/types/cloudfront.types.js                    
npm notice 133B  dist/types/cloudfront.types.js.map                
npm notice 72B   dist/types/index.d.ts                             
npm notice 110B  dist/types/index.js                               
npm notice 111B  dist/types/index.js.map                           
npm notice 1.1kB dist/types/serverless.types.d.ts                  
npm notice 121B  dist/types/serverless.types.js                    
npm notice 133B  dist/types/serverless.types.js.map                
npm notice 767B  dist/utils/callback-promise.d.ts                  
npm notice 1.1kB dist/utils/callback-promise.js                    
npm notice 814B  dist/utils/callback-promise.js.map                
npm notice 59B   dist/utils/capitalize.d.ts                        
npm notice 355B  dist/utils/capitalize.js                          
npm notice 360B  dist/utils/capitalize.js.map                      
npm notice 1.6kB dist/utils/cloudfront-headers.d.ts                
npm notice 2.9kB dist/utils/cloudfront-headers.js                  
npm notice 3.1kB dist/utils/cloudfront-headers.js.map              
npm notice 281B  dist/utils/combine-result.d.ts                    
npm notice 452B  dist/utils/combine-result.js                      
npm notice 419B  dist/utils/combine-result.js.map                  
npm notice 240B  dist/utils/config.builder.d.ts                    
npm notice 344B  dist/utils/config.builder.js                      
npm notice 320B  dist/utils/config.builder.js.map                  
npm notice 87B   dist/utils/context.builder.d.ts                   
npm notice 602B  dist/utils/context.builder.js                     
npm notice 480B  dist/utils/context.builder.js.map                 
npm notice 260B  dist/utils/convert-headers.d.ts                   
npm notice 423B  dist/utils/convert-headers.js                     
npm notice 441B  dist/utils/convert-headers.js.map                 
npm notice 440B  dist/utils/convert-to-cf-event.d.ts               
npm notice 706B  dist/utils/convert-to-cf-event.js                 
npm notice 671B  dist/utils/convert-to-cf-event.js.map             
npm notice 718B  dist/utils/deferred-promise.d.ts                  
npm notice 660B  dist/utils/deferred-promise.js                    
npm notice 557B  dist/utils/deferred-promise.js.map                
npm notice 424B  dist/utils/index.d.ts                             
npm notice 691B  dist/utils/index.js                               
npm notice 270B  dist/utils/index.js.map                           
npm notice 144B  dist/utils/is-response-result.d.ts                
npm notice 235B  dist/utils/is-response-result.js                  
npm notice 219B  dist/utils/is-response-result.js.map              
npm notice 69B   dist/utils/load-module.d.ts                       
npm notice 1.6kB dist/utils/load-module.js                         
npm notice 546B  dist/utils/load-module.js.map                     
npm notice 135B  dist/utils/to-result-response.d.ts                
npm notice 350B  dist/utils/to-result-response.js                  
npm notice 312B  dist/utils/to-result-response.js.map              
npm notice 1.4kB package.json                                      
npm notice === Tarball Details === 
npm notice name:          serverless-offline-edge-lambda          
npm notice version:       1.0.1                                   
npm notice filename:      serverless-offline-edge-lambda-1.0.1.tgz
npm notice package size:  21.4 kB                                 
npm notice unpacked size: 85.2 kB                                 
npm notice shasum:        b3dc96344a33f4f12d2530f4f61a71f2279ca178
npm notice integrity:     sha512-ArFIasCscqzdk[...]x5dZcuSET/k8w==
npm notice total files:   93                                      
npm notice 

Support server hot-reloading

TL;DR: As of version 1.0.2 the plugin seems to be missing a hot-reload feature.

I am developing a TypeScript-based L@E function. My serverless.yml is configured this way:

plugins:
  - serverless-offline-edge-lambda
  - serverless-plugin-typescript
custom:
   offlineEdgeLambda:
    path: .build
    originMap:
      - pathPattern: '*'
        target: dev_origins
. . .

When I start the server using sls offline start --port 80 --fileDir ./dev_origins I can successfully access my lambda at localhost:80 and everything works as expected.

However, when I change the source code of the lambda in TS, the server doesn't refresh with the latest version.
On every change I can see the log from the serverless-plugin-typescript:

Serverless: Compiling with Typescript...
Serverless: Typescript compiled.

I can also confirm that the compiled JS in .build (which I have specified as a path) is also updated.

Can the hot-reload feature be implemented, so that when Serverless: Typescript compiled. comes up, the server reloads with the freshly compiled version?

exception: cannot read property 'path' of undefined

I am getting the following exception starting offline with the plugin installed. It looks like I need to define custom.offlineEdgeLambda.path even though I don't need it. I am using the nodejs10.x runtime.

TypeError: Cannot read property 'path' of undefined at new BehaviorRouter (/home/hamish/.../node_modules/serverless-offline-edge-lambda/dist/behavior-router.js:53:70) at new OfflineEdgeLambdaPlugin (/home/hamish/.../node_modules/serverless-offline-edge-lambda/dist/index.js:16:23) at PluginManager.addPlugin (/home/hamish/.nodenv/versions/10.15.3/lib/node_modules/serverless/lib/classes/PluginManager.js:64:28) at plugins.forEach.plugin (/home/hamish/.nodenv/versions/10.15.3/lib/node_modules/serverless/lib/classes/PluginManager.js:106:14) at Array.forEach (<anonymous>) at PluginManager.loadPlugins (/home/hamish/.nodenv/versions/10.15.3/lib/node_modules/serverless/lib/classes/PluginManager.js:100:13) at PluginManager.loadServicePlugins (/home/hamish/.nodenv/versions/10.15.3/lib/node_modules/serverless/lib/classes/PluginManager.js:154:10) at PluginManager.loadAllPlugins (/home/hamish/.nodenv/versions/10.15.3/lib/node_modules/serverless/lib/classes/PluginManager.js:95:10) at pluginManager.loadConfigFile.then.then (/home/hamish/.nodenv/versions/10.15.3/lib/node_modules/serverless/lib/Serverless.js:78:28) From previous event: at Serverless.init (/home/hamish/.nodenv/versions/10.15.3/lib/node_modules/serverless/lib/Serverless.js:76:8) at initializeErrorReporter.then (/home/hamish/.nodenv/versions/10.15.3/lib/node_modules/serverless/bin/serverless.js:43:10) at runCallback (timers.js:705:18) at tryOnImmediate (timers.js:676:5) at processImmediate (timers.js:658:5) at process.topLevelDomainCallback (domain.js:120:23) From previous event: at /home/hamish/.nodenv/versions/10.15.3/lib/node_modules/serverless/bin/serverless.js:30:6 at Object.<anonymous> (/home/hamish/.nodenv/versions/10.15.3/lib/node_modules/serverless/bin/serverless.js:71:7) at Module._compile (internal/modules/cjs/loader.js:701:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10) at Module.load (internal/modules/cjs/loader.js:600:32) at tryModuleLoad (internal/modules/cjs/loader.js:539:12) at Function.Module._load (internal/modules/cjs/loader.js:531:3) at Function.Module.runMain (internal/modules/cjs/loader.js:754:12) at startup (internal/bootstrap/node.js:283:19) at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)

Serverless framework version 3 deprecation notice

What

Looks like this plugin uses an older way of defining parameters for framework version v3 .
(See: https://serverless.com/framework/docs/deprecations/#CLI_OPTIONS_SCHEMA_V3)

 deprecation triggered in the last command:

CLI options definitions were upgraded with "type" property (which could be one of "string", "boolean", "multiple"). Below listed plugins do not predefine type for introduced options:
 - OfflineEdgeLambdaPlugin for "port", "cloudfrontPort", "disableCache", "cacheDir", "fileDir"
Please report this issue in plugin issue tracker.
Starting with next major release, this will be communicated with a thrown error.

Why might this be a problem?

This will lead to full incompatibility with serverless framework V4.

How can this be fixed

Possibly detecting the framework version and registering using the newer method on v3+

content-type image/jpeg encoded with base64 not encoded correctly.

If an edge lambda returns a base64 encoded string with a content-type of image/jpeg, the image is not displayed in the browser correctly.

The issue is in behaviour-router.ts here:

if (response.bodyEncoding === 'base64') {
  res.end(Buffer.from(response.body ?? '', 'base64').toString('utf-8'));
} else {
  res.end(response.body);
}

The code should not be doing the final toString('utf8') as cloudfront edge converts this to a binary buffer, not a utf-8 string. Images are encoded and displayed correctly if the toString is left off.

I think if the response.headers['content-type'] is text, it could convert to a string, but why would you base64 encode it if it was of type text? Base64 encoding would only be useful for images I think.

I will submit a pull request for the change and we can discuss it further there.

Boom library and other faulty error handling causing requests to hang in may failure conditions

What?

The boom library seems to either be problematic or has implemented a number of breaking changes since this package was last published. A wide range of errors will cause requests to the offline dev handler to hang requests. Hanging requests are exceedingly hard to debug without tearing into the package and working out why.

How?

In many catch statements reraise unexpected errors cause the "catch step" to fail entierley. This causes the request to hang and the exception to be blackholes.

What we might want to do about this?

We may want to try to simplify the errors and remove / reduce use of the boom library given we only seem to use it for transporting 500 and 404 errors. It seems rather heavy for what it offers. An internal set of 2 errors we can add to the src/errors directory will likely fill in the same role without causing the wide range of issues the boom package is seemingly causing.

does not handle list of distributions

If functions.<function>.lambdaAtEdge is a list of distributions, rather than a single distribution, the plugin does not handle it;

functions:
  lambda:
    handler: handlers.onViewerRequest
    lambdaAtEdge:
      - distribution: 'WebsiteDistribution'
        eventType: 'viewer-request'
        pathPattern: '*'

Results in:

$ sls offline start
Serverless: CloudFront Offline listening on port 8080
Serverless: Cache directory: file:///tmp/edge-lambda
Serverless: Files directory: file:///tmp/edge-lambda

Serverless: Lambdas for path pattern *: 
Serverless: viewer-request => 
Serverless: origin-request => 
Serverless: origin-response => 
Serverless: viewer-response => 

I am attaching the same Lambda to multiple different CloudFront paths (cache behaviours) so I need to list them all for each function.

Hot reloading not working with serverless-plugin-typescript

Hello,
Thank you for this wonderful plugin, however I cannot get the hot reloading working. I did everything like in the docs, but I have typescript re-transpiling on change but then the server doesn’t pick up that change, doesn’t reload. Here is serverless.yml file I’m using

plugins:
  - serverless-plugin-typescript
  - serverless-offline-edge-lambda
  - "@silvermine/serverless-plugin-cloudfront-lambda-edge"

functions:
  lambda:
    handler: src/handlers.lambda1
    lambdaAtEdge:
      eventType: "origin-request"
      pathPattern: "*"
      includeBody: false

custom:
  offlineEdgeLambda:
    path: ".build"

Also, it would be nice to have a working example with deployment working as well.

Configuration error due to serverless 2.0 upgrade schema validation

Serverless framework 2.x introduced schema validation. Many plugins have already upgraded their code to remain compatible with SLS 2.0. Won't this plugin do that same please?

 Configuration error: 
       at 'functions.NikolmabdaImgoozr': unrecognized property 'lambdaAtEdge'
       at 'functions.NikolmabdaImgoozrOriginResponse': unrecognized property 'lambdaAtEdge'

Note: You need configValidationMode: error in the top level of your serverless.yml, else this will get reported as a warning instead.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.