Детальный обзор Well-known Symbols
Доброго времени суток, друзья!
Символ (Symbol) — это примитивный тип данных, представленный в ECMAScript2015 (ES6), позволяющий создавать уникальные идентификаторы: const uniqueKey = Symbol(‘SymbolName’).
Вы можете использовать символы в качестве ключей для свойств объектов. Символы, которые JavaScript обрабатывает особым образом, называются хорошо известными символами (Well-known Symbols). Эти символы используются встроенными алгоритмами JavaScript. Например, Symbol.iterator используется для перебора элементов массивов, строк. Его также можно использовать для определения собственных функций-итераторов.
Данные символы играют важную роль, поскольку позволяют осуществлять тонкую настройку поведения объектов.
Будучи уникальными, использование символов в качестве ключей объектов (вместо строк) позволяет легко добавлять объектам новый функционал. При этом, не нужно беспокоиться о возникновении коллизий между ключами (поскольку каждый символ уникален), что может стать проблемой при использовании строк.
Если мы говорим о том, что объект имеет метод @@iterator, значит, объект содержит свойство под названием Symbol.iterator, представленное функцией: < [Symbol.iterator]: function() < >>.
1. Краткое введение в символы
Символ — это примитивный тип (такой как число, строка или логическое значение), уникальный и неизменяемый (иммутабельный).
Для создания символа необходимо вызвать функцию Symbol() с опциональным аргументом — названием или, точнее, описанием символа:
mySymbol и namedSymbol — это символы-примитивы. namedSymbol имеет название ‘myName’, которое, обычно, используется в целях отладки кода.
При каждом вызове Symbol() создается новый уникальный символ. Два символа являются уникальными (или особыми), даже если имеют одинаковые названия:
Символы могут быть ключами объектов. Для этого в объектном литерале или определении класса необходимо использовать синтаксис вычисляемых свойств ([symbol]):
Свойства-символы не могут быть получены с помощью Object.keys() или Object.getOwnPropertyNames(). Для доступа к ним нужно использовать специальную функцию Object.getOwnPropertySymbols().
Использование хорошо известных символов в качестве ключей позволяет изменять поведение объектов.
Хорошо известные символы доступны как неперечисляемые, неизменяемые и ненастраиваемые свойства объекта Symbol. Для их получения следует использовать точечную нотацию: Symbol.iterator, Symbol.hasInstance и т.д.
Вот как можно получить список хорошо известных символов:
Object.getOwnPropertyNames(Symbol) возвращает список собственных свойств объекта Symbol, включая хорошо известные символы. Разумеется, типом Symbol.iterator является symbol.
2. @@iterator, позволяющий делать объекты перебираемыми (итерируемыми)
Symbol.iterator — это, пожалуй, наиболее известный символ. Он позволяет определять, как объект должен перебираться с помощью инструкции for-of или spread-оператора (и должен ли он перебираться вообще).
Многие встроенные типы, такие как строки, массивы, карты (maps), наборы или коллекции (sets) являются итерируемыми по умолчанию, поскольку у них есть метод @@iterator:
Переменная myStr содержит примитивную строку, у которой имеется свойство Symbol.iterator. Данное свойство содержит функцию, используемую для перебора символов строки.
В следующем примере мы создаем итерируемый объект myMethods, позволяющий перебирать его методы:
methodsIterator() — это функция, которая возвращает итератор < next: function() < >>. В объекте myMethods определяется вычисляемое свойство [Symbol.iterator] со значением methodsIterator. Это делает объект перебираемым с помощью цикла for-of. Методы объекта также можно получить с помощью [. myMethods]. Такой объект можно преобразовать в массив с помощью Array.from(myMethods).
Создание итерируемого объекта можно упростить с помощью функции-генератора. Данная функция возвращает объект Generator, соответствующий протоколу перебора.
Создадим класс Fibonacci с методом @@iterator, генерирующим последовательность чисел Фибоначчи:
*[Symbol.iterator]() < >определяет метод класса — функцию-генератор. Экземпляр Fibonacci соответствует протоколу перебора. spread-оператор вызывает метод @@iterator для создания массива чисел.
Если примитивный тип или объект содержит @@iterator, он может быть использован в следующих сценариях:
3. @@hasInstance для настройки instanceof
По умолчанию оператор obj instanceof Constructor проверяет, имеется ли в цепочке прототипов obj объект Constructor.prototype. Рассмотрим пример:
obj instanceof Constructor возвращает true, поскольку прототипом obj является Constructor.prototype (как результат вызова конструктора). instanceof при необходимости обращается к цепочке прототипов, поэтому obj instanceof Object также возвращает true.
Иногда в приложении требуется более строгая проверка экземпляров.
К счастью, у нас имеется возможность определить метод @@hasInstance для изменения поведения instanceof. obj instanceof Type является эквивалентом Type[Symbol.hasInstance](obj).
Давайте проверим, являются ли переменные итерируемыми:
Класс Iterable содержит статический метод @@hasInstance. Данный метод проверяет, является ли obj перебираемым, т.е. содержит ли он свойство Symbol.iterator. arr и str являются итерируемыми, а num нет.
4. @@toPrimitive для преобразования объекта в примитив
Используйте Symbol.toPrimitive для определения свойства, значением которого является функция преобразования объекта в примитив. @@toPrimitive принимает один параметр — hint, которым может быть number, string или default. hint указывает на тип возвращаемого значения.
Усовершенствуем преобразование массива:
Метод @@toPrimitive используется для представления объекта в виде примитивного типа:
5. @@toStringTag для создания стандартного описания объекта
Используйте Symbol.toStringTag для определения свойства, значением которого является строка, описывающая тип объекта. Метод @@toStringTag используется Object.prototype.toString().
Спецификация определяет значения, возвращаемые Object.prototype.toString() по умолчанию, для многих типов:
Эти типы не имеют свойства Symbol.toStringTag, поскольку алгоритм Object.prototype.toString() оценивает их особым образом.
Рассматриваемое свойство определяется в таких типах, как символы, функции-генераторы, карты, промисы и др. Рассмотрим пример:
В случае, когда объект не относится к группе со стандартным типом и не содержит свойства @@toStringTag, возвращается Object. Разумеется, мы можем это изменить:
Экземпляр класса SimpleClass не имеет свойства @@toStringTag, поэтому Object.prototype.toString() возвращает [object Object]. В конструкторе класса MyTypeClass экземпляру присваивается свойство @@toStringTag со значением MyType, поэтому Object.prototype.toString() возвращает [object MyType].
Обратите внимание, что @@toStringTag был введен в целях обеспечения обратной совместимости. Его использование нежелательно. Для определения типа объекта лучше использоваить instanceof (совместно с @@hasInstance) или typeof.
6. @@species для создания производного объекта
Используйте Symbol.species для определения свойства, значением которого является функция-конструктор, используемая для создания производных объектов.
Значением @@species многих конструкторов являются сами конструкторы:
Во-первых, обратите внимание, что производным называется объект, возвращаемый после совершения определенной операции с исходным объектом. Например, вызов map() возвращает производный объект — результат преобразования элементов массива.
Обычно, производные объекты ссылаются на тот же конструктор, что и исходные объекты. Но порой возникает необходимость в определении другого конструктора (возможно, одного из стандартных классов): вот где может помочь @@species.
Предположим, что мы расширяем конструктор Array с помощью дочернего класса MyArray для добавления некоторых полезных методов. При этом мы хотим, чтобы конструктором производных объектов экземпляра MyArray был Array. Для этого необходимо определить вычисляемое свойство @@species со значением Array:
В MyArray определено статическое вычисляемое свойство Symbol.species. Оно указывает, что конструктором производных объектов должен быть конструктор Array. Позже при фильтрации элементов массива array.filter() возвращает Array.
Вычисляемое свойство @@species используется методами массивов и типизированных массивов, такими как map(), concat(), slice(), splice(), возвращающими производные объекты. Использование данного свойства может быть полезным для расширения карт, регулярных выражений или промисов с сохранением оригинального конструктора.
7. Создание регулярного выражения в форме объекта: @@match, @@replace, @@search и @@split
Прототип строки содержит 4 метода, принимающих регулярные выражения в качестве аргумента:
Любопытно, что прототип RegExp содержит указанные методы, также определенные с помощью символов:
В следующем примере мы определяем класс, который может использоваться вместо регулярного выражения:
В классе Expression определяются методы @@match, @@replace, @@search и @@split. Затем экземпляр этого класса — sunExp используется в соответствующих методах вместо регулярного выражения.
8. @@isConcatSpreadable для преобразования объекта в массив
Symbol.isConcatSpreadable представляет собой логическое значение, указывающее на возможность преобразования объекта в массив с помощью метода Array.prototype.concat().
По умолчанию, метод concat() извлекает элементы массива (раскладывает массив на элементы, из которых он состоит) при объединении массивов:
Для объединения двух массивов letters передается в качестве аргумента методу concat(). Элементы массива letters становятся частью результата объединения: [‘c’, ‘d’, ‘e’, ‘a’, ‘b’].
Для того, чтобы предотвратить разложение массива на элементы и сделать массив частью результата объединения как есть, свойству @@isConcatSpreadable следует присвоить значение false:
В противоположность массиву, метод concat() не раскладывает на элементы массивоподобные объекты. Это поведение также можно изменить с помощью @@isConcatSpreadable:
9. @@unscopables для доступа к свойствам посредством with
Symbol.unscopables — это вычисляемое свойство, собственные имена свойств которого исключаются из объекта, добавляемого в начало цепочки областей видимости с помощью инструкции with. Свойство @@unscopables имеет следующий формат: < propertyName: >.
ES6 определяет @@unscopables только для массивов. Это сделано в целях сокрытия новых методов, которые могут перезаписать одноименные переменные в старом коде:
Мы можем получить доступ к методу concat() в теле with, поскольку данный метод не содержится в свойстве @@unscopables. Метод entries() указан в этом свойстве и имеет значение true, что делает его недоступным внутри with.
@@unscopables был введен исключительно для обеспечения обратной совместимости со старым кодом, где используется инструкция with (которая признана устаревшей и запрещена в строгом режиме).
Надеюсь, вы нашли для себя что-нибудь интересное. Благодарю за внимание.
Let’s Encrypt и nginx: настройка в Debian и Ubuntu
Если вдруг вся эта история прошла мимо вас, Let’s Encrypt — центр сертификации от некоммерческой организации ISRG, существующий при поддержке EFF и многих компаний, взявшей на себя миссию дать людям бесплатные SSL/TLS сертификаты для сайтов и серверов. Сертификаты от Let’s Encrypt уже используются на более чем 10 миллионах доменов.
Кроме очевидной бесплатности у сертификатов от Let’s Encrypt есть особое, отсутствующее у любых других коммерческих сертификационных центров, достоинство: если вы однажды получили сертификат от Let’s Encrypt, то, при прочих равных, это навсегда. Не нужно раз в год-два вручную обновлять сертификаты. Не нужно вообще вспоминать что сертификаты где-то есть. Получил, настроил и забыл!
Внимательный читатель сразу захочет возразить: как же так, ведь известно что сертификаты выдаются со сроком действия в три месяца? Всё дело в автоматическом обновлении сертификатов, которое возможно при полном отсутствии действий со стороны человека.
Организации автоматического обновления сертификатов в статье уделено пристальное внимание, с тем чтобы вы могли в полной мере оценить это принципиальное преимущество Let’s Encrypt.
Почему эта статья
На сайте EFF есть краткие инструкции по использованию Certbot, рекомендуемой программы для получения сертификатов, но они скорей рассчитаны на тех, кто заходит на свой сервер по SSH лишь по острой необходимости. Более подробная документация тоже есть, но пока всю ее прочитаешь и найдешь всё то, что действительно нужно знать… К тому же, в ней не рассмотрены некоторые важные стратегические вопросы использования сертификатов.
Очевидно, нужна короткая и понятная инструкция для тех, кто привычен к серверной консоли, но хочет во всём разобраться без излишних трат времени.
Содержание
Из этой статьи вы узнаете.
Caveat emptor
В инструкциях ниже я исхожу из того что ваши сайты будут использовать SNI. Это расширение протокола TLS позволяет браузерам сообщить желаемое имя сайта до получения и проверки SSL сертификата от сервера. Благодаря SNI вы можете разместить сколько угодно сайтов за HTTPS на одном IP. Но не всё так просто — иначе бы зачем я об этом писал?
Есть ряд старых браузеров в принципе не поддерживающих SNI. В их число входят любые версии IE в уже заброшенном Windows XP, встроенный браузер в Android 2.3 и 2.2 из 2010 года, а также некоторые другие более экзотические браузеры и библиотеки типа Java версии 1.6 и Python до версии 2.7.9.
Если вы всё-таки хотите чтобы ваш сайт открывался в IE в Windows XP, то одним отказом от SNI эта проблема не решается. Нужно специальным образом подбирать шифры, уже отказываясь от forward secrecy и рискуя получить низкую оценку от SSL Labs. Как можно догадаться, этот вопрос заслуживает отдельного обсуждения хотя бы потому что пользователям IE под XP можно посочувствовать — у них уже не открывается половина интернета!
Еще год назад от перехода на SNI вас могла бы удержать ограниченная поддержка этой технологии некоторыми поисковыми ботами типа Bing, но сейчас, с появлением десятков сайтов с бесплатными сертификатами от Cloudflare, что без SNI не открываются, бот Bing (что легко проверить), и боты других основных поисковиков, пришли в согласие с реальностью. Сейчас за это можно не волноваться. Отмечу, что у Googlebot таких проблем не было никогда.
Другим поводом для волнений могут быть различные средства доступа к API вашего сайта. Если у вас давно есть API, то есть небольшой шанс что среди ваших клиентов есть какие-то, использующие устаревшие версии Java или Python. Если у вас таких нет, то не о чем переживать. Если же есть — мои соболезнования.
Почему лучше рассчитывать на SNI?
Это просто. Вам не нужно постоянно держать в голове факты о выданных сертификатах. Для какого домена сертификат был выдан первым. К какому сертификату нужно добавлять еще домены. И так далее… Ни о чем таком со SNI не нужно думать.
Например, так можно посмотреть домены в сертификате Тематических Медиа:
На момент написания статьи эта команда выведет подробный список всевозможных доменов ТМ:
Никакой секретности и никаких тайн. Вы этого хотите?
Установка Certbot
Если вы читаете этот текст из будущего, когда Certbot уже есть в Debian stable и Ubuntu без обиняков и оговорок, то всё просто:
Либо используйте aptitude или другой пакетный менеджер вашего дистрибутива.
Установка в Jessie
Если у вас еще в ходу актуальный на конец 2016 года Debian stable «jessie», то всё лишь немного сложнее.
Нужно подключить Debian Backports, добавив строчку в /etc/apt/sources.list :
Теперь можно устанавливать с указанием источника:
(Раздел актуален пока только stretch не стал stable.)
Ubuntu версий ниже 16.10 (yakkety)
Другой дистрибутив
Если у вас какой-то другой дистрибутив, то дополнительные инструкции по установке есть на официальном сайте Certbot. Если обходиться без пакетного менеджера, то обычно установка сводится к.
Certbot и webroot
Мы будем получать сертификаты по методу webroot без перенастройки или остановки веб-сервера, под которым подразумевается nginx. Нам нужен какой-то каталог, в который certbot будет писать свои файлы, и какой должен быть доступен из сети удостоверяющему серверу согласно протокола ACME.
Чтобы не писать каждый раз длинную строку из опций, а еще лучше — не вспоминать о них, запишем основные настройки в файл конфигурации, который certbot ожидает найти в /etc/letsencrypt/cli.ini :
Последняя директива нужна чтобы избавить нас от прелестей и красивостей ncurses, что нужно чтобы вы могли сравнить вывод команд здесь, в этой статье, и у себя.
Также нам нужно мягко перезагрузить nginx (без перерыва в обслуживании) при успешном обновлении сертификатов. Ничего не мешает в этот же момент перезапускать и другие сервисы вроде Postfix, использующие полученные сертификаты. Команды указываются через точку с запятой.
Если вы видите такую ошибку:
Что будет делать Certbot
Ожидается что certbot будет создавать необходимые для проверки прав на домен файлы в подкаталогах ниже по иерархии к указанному. Вроде таких:
Эти файлы должны будут быть доступны из сети на целевом домене по крайней мере по HTTP:
Для следующих проверок создадим какой-то такой файл:
Регистрация в Let’s Encrypt
Регистрацию нужно сделать только один раз:
Здесь ничего сложного.
Подготовим nginx к получению сертификатов
В общем случае для получения сертификата необходимо во всех блоках server добавить следующий блок до других блоков location :
Понятно, что вписывать для каждого сайта такой блок явно — это моветон, потому создадим файл /etc/nginx/acme с содержанием блока выше.
Затем для каждого домена и поддомена, для которых нужно получить сертификаты, в блоке server перед всеми блоками location укажем:
Хосты-редиректоры (например, с голого домена на www) можно пропустить. ACME сервер обязан учитывать стандартную переадресацию. Подробней об этом ниже.
Перезагрузим nginx и проверим что наш тестовый файл виден:
После проверки лучше удалить тестовый файл — certbot любит удалять за собой всё лишнее, а такой файл будет мешать и вызывать сообщение об ошибке (Unable to clean up challenge directory).
О переадресации с кодами 301 и 302
Как было уже сказано, ACME сервер Boulder учитывает переадресацию с кодами 301 и 302. В этом смысле не имеет значения где, в конечном счете, находятся файлы, требуемые для прохождения проверок. Переадресация возможна даже на нестандартные порты, без ограничений по конечному протоколу HTTP или HTTPS. Сами Let’s Encrypt рекомендуют использовать переадресацию для создания единой точки проверки прав на домены.
Если вы можете получить эти файлы с помощью curl с ограничением в десять переадресаций, то и Boulder эти файлы увидит. Не должно быть никаких ограничений по IP адресам.
Это удобно если у вас сложная структура переадресаций между разными версиями сайтов. Должно быть достаточно подключить тот блок с location только на основном сайте для получения сертификатов для всех остальных.
Проверка всегда начинается с запроса по протоколу HTTP на 80 порту.
Если у вас уже всё зашифровано.
Другое дело что можно сократить путь и подключить наш блок с location в умолчальном сервере для 80 порта, который делает переадресацию на HTTPS. Тогда не нужно будет ничего дописывать в конфиги отдельных сайтов.
Пример конфигурации такого переадресующего всё-подряд-на-HTTPS сервера:
Сервер запускаем явно на внешнем IP чтобы не перенастраивать Apache на другой порт. Если для вас это не проблема, то указание имени сервера в директиве listen можно пропустить.
Если нужно получить сертификат для домена без сайта.
Типичный пример — сертификат для выделенных под SMTP или IMAP серверов, на которых вообще нет каких-то сайтов. Либо используйте универсальный переадресатор что выше, либо.
К сожалению, протокол ACME требует чтобы такой сервер был доступен во время каждой проверки. Это практически эквивалентно постоянной доступности, ввиду требования получения и обновления сертификатов без перезагрузки сервера. Не удаляйте такой конфиг после получения сертификата.
Если у вас только Apache2.
Если у вас Apache2, а перейти на всеми любимый nginx возможности нет, то… Добавьте следующие строчки в /etc/apache2/conf-available/certbot.conf :
И обязательно проверьте, так:
Существует много причин почему такая схема может у вас в Apache2 не заработать. Пары экранов текста не хватит чтобы описать их все. Не серчайте — статья про nginx.
Получаем сертификаты
У Let’s Encrypt есть лимиты на количество обращений за сертификатами, потому сначала попробуем получить необходимый сертификат в режиме для тестов:
В конце программа должна отчитаться об успешной работе:
Теперь можно смело получать сертификат уже в самом деле. Не забудьте явно указать все необходимые поддомены, такие как www.
Ура! С получением сертификата закончено!
Если нужно добавить поддомен или домен в сертификат
Вам будет безальтернативно предложено добавить этот домен в сертификат. Если хочется избежать вопросов, то можно сразу указать одобряющий такое поведение ключ:
Операцию можно повторять.
Проверим полученный сертификат
Убедимся что полученный сертификат — именно тот, что нам нужен:
Или, если подробности вам не нужны:
Команда должна вывести список доменов в сертификате.
Установка и использование сертификатов
Certbot не перезаписывает сертификаты, а заменяет их ссылками на самые актуальные варианты сертификатов в определенном каталоге, одноименном с первым доменом сертификата (т.е. CN ).
Давайте посмотрим что за файлы у нас есть:
С этим знанием мы можем задать настройки SSL для nginx:
Как видите, cert.pem нигде в конфиге не используется, и это не ошибка. Для nginx он не нужен.
Полный рабочий пример конфига:
Конфиг для переадресации с голого домена без www:
Подразумевается что вы используете какой-то локальный сервер для кеширования DNS запросов. Если это не так, то 127.0.0.1 в директиве resolver нужно заменить на IP используемого DNS сервера.
Perfect Forward Secrecy
Если вы переживаете что Certbot может утащить ключи от вашего сертификата не смотря на открытые исходные коды, а значит, в теории, какие-то злодеи смогут расшифровать весь трафик, то спешу вас успокоить. Если для соединения с вашим сайтом используются шифры из семейств DHE и ECDHE, то утечка ключа не позволит расшифровать трафик. В этих шифрах ключ сертификата используется только для подтверждения подлинности, и не используется в качестве ключа для шифрования. Все современные браузеры поддерживают эти шифры.
Если для ECDHE на эллиптических кривых ничего не нужно делать, то для DHE можно было бы использовать усиленные параметры. Лучше всего будет отключить DHE вообще.
Если по какой-то причине без DHE вы не можете обойтись, то сначала создадим параметры:
Потом пропишем в /etc/nginx/conf.d/ssl_dhparam.conf одной строкой:
Продление сертификатов
Сертификаты выдаются на три месяца. Не на полгода, не на год, а лишь на три месяца. Естественно это вызывает вопросы. Нужно ли проходить всю эту процедуру через три месяца? Нужно ли это делать всегда до искончания веков? Может стоит всё-таки вложиться в платный сертификат чтобы забыть об этом всем и не воспоминать пару лет?
Но нет, не спешите искать платежные средства! Как и было обещано в начале статьи, с обновлением сертификатов проблем нет.
Если у вас Debian и systemd, то посмотрите эти инструкции.
Как это работает
Например, были у вас на сервере были сайты www.example.com и shop.example.com, проходящие под одним сертификатом, но потом вы перенесли shop.example.com на другой сервер. Если такой ключ не указать, то Certbot упадет с ошибкой при попытке подтвердить владение shop.example.com, не получив для вас вообще никакого сертификата. Сертификат истечет и ваш сайт уйдет в оффлайн. С этим ключом вы всё же получите сертификаты хотя бы для частичного набора доменов, оставив ваши сайты в сети.