├── .gitignore ├── README.md ├── cgi-bin └── hello-world.pl ├── handler.py └── serverless.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Distribution / packaging 2 | .Python 3 | env/ 4 | build/ 5 | develop-eggs/ 6 | dist/ 7 | downloads/ 8 | eggs/ 9 | .eggs/ 10 | lib/ 11 | lib64/ 12 | parts/ 13 | sdist/ 14 | var/ 15 | *.egg-info/ 16 | .installed.cfg 17 | *.egg 18 | __pycache__ 19 | 20 | # Serverless directories 21 | .serverless 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Serverless CGI 2 | 3 | Because [serverless is just cgi-bin](https://twitter.com/benschwarz/status/743180671720398848). 4 | And I just had surgery so painkiller made this seem like a hilarious idea. 5 | 6 | This is a [serverless framework](https://serverless.com) and AWS Lambda based 7 | simple CGI service. Just drop your scripts into the `cgi-bin` directory. 8 | 9 | ## Example use 10 | Here's how to install and deploy your CGI script (`hello-world.pl` in this 11 | case). 12 | ``` 13 | $ npm i -g serverless 14 | $ sls install https://github.com/dschep/serverless-cgi 15 | $ sls deploy 16 | Serverless: Packaging service... 17 | Serverless: Excluding development dependencies... 18 | Serverless: Uploading CloudFormation file to S3... 19 | Serverless: Uploading artifacts... 20 | Serverless: Uploading service .zip file to S3 (4.85 KB)... 21 | Serverless: Validating template... 22 | Serverless: Updating Stack... 23 | Serverless: Checking Stack update progress... 24 | .............. 25 | Serverless: Stack update finished... 26 | Service Information 27 | service: sls-cgi 28 | stage: dev 29 | region: us-east-1 30 | api keys: 31 | None 32 | endpoints: 33 | ANY - https://u5j8jngzyc.execute-api.us-east-1.amazonaws.com/dev/cgi-bin/{path+} 34 | functions: 35 | cgi-bin: sls-cgi-dev-cgi-bin 36 | Serverless: Removing old service versions... 37 | $ http -v https://u5j8jngzyc.execute-api.us-east-1.amazonaws.com/dev/cgi-bin/hello-world.pl 38 | GET /dev/cgi-bin/hello-world.pl HTTP/1.1 39 | Connection: keep-alive 40 | Host: u5j8jngzyc.execute-api.us-east-1.amazonaws.com 41 | Accept-Encoding: gzip, deflate 42 | Accept: */* 43 | User-Agent: HTTPie/0.9.8 44 | 45 | 46 | 47 | HTTP/1.1 200 OK 48 | Content-Type: application/json 49 | Content-Length: 11 50 | Connection: keep-alive 51 | Date: Wed, 02 Aug 2017 17:24:25 GMT 52 | x-amzn-RequestId: 6e93728b-77a7-11e7-a743-f58b9a3ef9b0 53 | X-Amzn-Trace-Id: sampled=0;root=1-59820ac9-e30697d6cf113580ba23f338 54 | X-Cache: Miss from cloudfront 55 | Via: 1.1 f19281f08e79aa6c6634266c50732dd5.cloudfront.net (CloudFront) 56 | X-Amz-Cf-Id: RFLj0hA_Jm8pSVW60EvrOxqlkXiS-eNSrNF8Kiz9e9W0zjF54FCadQ== 57 | 58 | Hello World 59 | ``` 60 | -------------------------------------------------------------------------------- /cgi-bin/hello-world.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | print "Content-type: text/plain\n\n"; 4 | 5 | print "Hello World" 6 | -------------------------------------------------------------------------------- /handler.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | from shutil import copyfile 4 | from subprocess import check_output 5 | from urllib.parse import urlencode 6 | 7 | 8 | def cgi(event, context): 9 | env = os.environ.copy() 10 | env.update(**{f'HTTP_{name.upper().replace("-", "_")}': value 11 | for name, value in event.get('headers', {}).items()}) 12 | 13 | script = event['pathParameters']['path'] 14 | 15 | env.update( 16 | SERVER_SOFTWARE='serverless-cgi/0.1', 17 | SERVER_NAME=event['headers'].get('Host', ''), 18 | GATEWAY_INTERFACE='CGI/1.1', 19 | SERVER_PROTOCOL='HTTP/1.1', 20 | SERVER_PORT=event['headers'].get('X-Forwarded-Port', '' ), 21 | REQUEST_METHOD=event['httpMethod'], 22 | PATH_INFO='', # TODO 23 | PATH_TRANSLATED='', # TODO 24 | SCRIPT_NAME='', # TODO 25 | QUERY_STRING=urlencode(event['queryStringParameters'] or {}), 26 | REMOTE_ADDR=event['requestContext']['identity']['sourceIp'], 27 | CONTENT_TYPE=event['headers'].get('Content-Type'), 28 | CONTENT_LENGTH=(len(event['body']) 29 | if event.get('body') is not None 30 | else 0), 31 | ) 32 | 33 | copyfile(f'./cgi-bin/{script}', f'/tmp/{script}') 34 | os.chmod(f'/tmp/{script}', 0o755) 35 | headers, body = check_output([f'/tmp/{script}']).decode().split('\n\n', 1) 36 | 37 | response = { 38 | "statusCode": 200, 39 | "body": body, 40 | } 41 | 42 | return response 43 | -------------------------------------------------------------------------------- /serverless.yml: -------------------------------------------------------------------------------- 1 | service: sls-cgi 2 | provider: 3 | name: aws 4 | runtime: python3.6 5 | functions: 6 | cgi-bin: 7 | handler: handler.cgi 8 | events: 9 | - http: 10 | path: "cgi-bin/{path+}" 11 | method: any 12 | --------------------------------------------------------------------------------