michimani.net

CloudFront Functions でサポートされているランタイムについて調べてみた

2021-05-13

CloudFront Functions でサポートされているランタイムは現時点で JavaScript (ECMAScript 5.1 準拠) となっていますが、「ECMAScript 5.1 !? いつの規格やねん、解散。」 となる前にあらためて公式ドキュメントを読んで詳細を確認してみます。

なお、内容については 2021/05/13 時点のものです。

目次

はじめに

今回の内容は、下記の公式ドキュメントの内容を自分なりにまとめたものになります。公式ドキュメント以上の内容はありません。

ECMAScript 5.1 compliant

CloudFront Functions の発表に歓喜したものの、サポートされているランタイムが JavaScript のみで且つ ECMAScript 5.1 compliant となっていて、残念に思った方は少なくないと思います。ただし、公式ドキュメントには下記のように書かれています。

The CloudFront Functions JavaScript runtime environment is compliant with ECMAScript (ES) version 5.1 and also supports some features of ES versions 6 through 9.

つまり ベースは ECMAScript 5.1 (2011/06)1 ですが、 ES 6 (2015)2 から ES 9 (2018)3 に含まれる一部の機能もサポートしており、さらに ES の仕様に含まれない非標準のメソッドも提供されています。

なので、 ES 5.1 以降の仕様で何が含まれているのか、非標準のメソッドって具体的に何が含まれているのか、気になったところを見ていきます。

ちなみに、 コンソールや AWS CLI などからランタイムを指定する場合は cloudfront-js-1.0 という名前になっています。

Core features

ES のコアとなる仕様についてです。

式と演算子

ES 5.1 のすべての演算子に加えて、 ES 7 に含まれる べき乗演算子 ** がサポートされています。

var side = 4;
var square = side ** 2;

console.log(square);

// 16

文と宣言

下記のステートメントがサポートされています。

宣言

let および const はサポートされていません (ES 6)

制御フロー

反復処理

その他

関数

ES 5.1 のすべての機能に加えて、 ES 6 に含まれる arrow functionrest parameter (残余引数) がサポートされています。

var sum = (...args) => {
  var total = 0;
  for (var i in args) {
    total += args[i];
  }
  return total;
}

console.log(sum(2,4,6));

// 12

Primitive objects

Object

ES 5.1 の Object メソッド (一部) に加えて、 ES 6 および ES 8 に含まれる下記のメソッドがサポートされています。

String

ES 5.1 の String メソッド (一部) に加えて、 ES 6、 ES 8、 ES 9 に含まれる下記のメソッドがサポートされています。

fromCodePoint

更に、下記の非標準メソッドもサポートされています。

用途がパッと思いつかなかったんですが、公式の example では JWT の検証として String.bytesFrom() を使ってました。

Validate a simple token in the request - Amazon CloudFront

Number

ES 5.1 のすべての Number メソッドと、 ES 6 に含まれる下記のメソッド/定数がサポートされています。

Built-in objects

Date

ES 5.1 の Date の機能がすべてサポートされていますが、下記の制限があります。

セキュリティの観点から、 Date のメソッドで返却される現在時刻は実行される Function のライフタイム内で常に一定で、その値は Function が起動した時点の時刻となります。
つまり、関数の開始と終了時点でそれぞれの時刻を取得して、その差分から関数の実行時間を取得するようなことはできません。

var start = Date.now();

/**
 * 何かしらの処理
 */

var end = Date.now();

console.log(`${start}\n${end}`);

// 1620857555194
// 1620857555194

Function

Function constructors はサポートされていません。

var hello = new Function('return "Hello";');
console.log(hello());

// Error Message: The CloudFront function associated with the CloudFront distribution is invalid or could not run. TypeError: function constructor is disabled in "safe" mode

var bye = function() {return 'Bye';};
console.log(bye());

// bye

(safe じゃないモードなら使えるのか…?)

Regular expressions

ES 5.1 のすべての RegExp の機能に加えて、 ES 9 に含まれる named capture groups がサポートされています。

var CURRY_RICE_RE = /ソース:(?<source>.*) 辛さ:(?<spicy>.*) ご飯の量:(?<rice>.*)/;
var menu = 'ソース:ポーク 辛さ:2辛 ご飯の量:300g';
var matched = CURRY_RICE_RE.exec(menu);

console.log(matched.groups.source);
console.log(matched.groups.spicy);
console.log(matched.groups.rice);

// ポーク
// 2辛
// 300g

Array

ES 5.1 の Array メソッド (一部) に加えて、 ES 6 および ES 7 に含まれる下記のメソッドがサポートされています。

var currySource = Array.of('ポーク', 'ビーフ');
console.log(currySource);

// ['ポーク','ビーフ']

console.log(currySource.copyWithin(0, 1, 2));
// ['ビーフ','ビーフ']

console.log(currySource.fill('ハヤシ', 1, 2));
// ['ビーフ','ハヤシ']

console.log(currySource.find(element => element = 'ビーフ'));
// ビーフ

console.log(currySource.findIndex(element => element = 'ビーフ'));
// 0

console.log(currySource.includes('キーマ'));
// false

Typed arrays

ES 6 に含まれる 型付き配列 がサポートされています。

var int8array = new Int8Array(2);
int8array[0] = 0;
console.log(int8array);

int8array[1] = 'string';
console.log(int8array);

int8array[1] = 1;
console.log(int8array);

// Int8Array [0,0]
// Int8Array [0,0]
// Int8Array [0,1]

Built-in modules

Built-in modules として cryptoquerystring がサポートされており、それぞれ require で読み込むことで使用できます。

Crypto (hash and HMAC)

crypto モジュールでは、標準的なハッシュおよびハッシュベースのメッセージ認証コード (HMAC) のヘルパが提供されています。下記のメソッドがサポートされており、それぞれ Node.js の対応するメソッド4 と同じ挙動となります。

Hashing methods

var crypto = require('crypto');

var text = 'I love curry.';
var hash = crypto.createHash('sha256'); // or md5, sha1
hash.update(text);
var digest = hash.digest('hex'); // or base64, base64url

console.log(`${text}\n${digest}`);

// I love curry.
// 74dc57666d14c957cf622ad110f1c8d88c0f3390ee057333e97bcaf0c50ac82a

HMAC methods

var crypto = require('crypto');

var text = 'I love curry.';
var hmac = crypto.createHmac('sha256', 'secret key hoge'); // or md5, sha1
hmac.update(text);
var digest = hmac.digest('hex'); // or base64, base64url

console.log(`${text}\n${digest}`);

// I love curry.
// 2e3ef82b9c4265d77b977ab9d778663e240da3737e0059ca82c0f93b49f4ddd1

Query string

querystring モジュールでは、 URL 内のクエリ文字列を解析して操作するためのメソッドが提供されています。下記のメソッドがサポートされており、それぞれ Node.js の対応するメソッド5 と同じ挙動となります。
ただ、 CloudFront Functions に渡されるイベントオブジェクトは自動的にパースされるので、このモジュールを使う場面はほぼないです。

var qs = require('querystring');

var queryString = 'source=beef&rice=300&topping=egg&topping=garlic';
var parsed = qs.parse(queryString);
console.log(JSON.stringify(parsed, null, 2));

// {
//   "source": "beef",
//   "rice": "300",
//   "topping": [
//     "egg",
//     "garlic"
//   ]
// }

var params = {
  source: 'pork',
  rice: '500',
  topping: [
    'seafood'
  ]
}
var paramsString = qs.stringify(params);
console.log(paramsString);

// source=pork&rice=500topping=seafood

まとめ

CloudFront Functions でサポートされているランタイムについて、公式ドキュメントの内容を読んでみた話でした。

ベースは ECMAScript 5.1 ですが それ以降の ES 9 までの内容も含まれているので、 ECMAScript 5.1 という文字だけで気を落とすのは早そうです。そもそも CloudFront Functions では外部ネットワーク接続できなかったりメモリや実行時間の制限が厳しいこともあり、古い仕様でもなんとかなっているのかなと思いました。とはいえ var で変数宣言するのはちょっと気持ち悪いです。

今後ランタイムは更新されているかもしれませんが、現状では諸々の制限の中で処理を実装する上では ECMAScript 5.1 準拠であっても困ることはなさそうです。


  1. ECMAScript Language Specification - ECMA-262 Edition 5.1  ↩︎

  2. ECMAScript 2015 Language Specification – ECMA-262 6th Edition  ↩︎

  3. ECMAScript® 2018 Language Specification  ↩︎

  4. Crypto | Node.js v16.1.0 Documentation  ↩︎

  5. Query string | Node.js v16.1.0 Documentation  ↩︎


comments powered by Disqus