# Веб 1 лабораторная работа
## HTTP
## REST
Приложение на ресте компонуется из нескольких ресурсов, каждый из которых отображается на отдельный URI/URL
В случае с рест у нас есть набор процедур, отображенных на определенных урлах (каждый на своем) (при этом считается, что если отправить запрос по 2 одинаковым урлам, то мы получим одно и то же)
## RPC
Подход основанный на вызове удаленных процедур. В этом случае наш набор процедур
## URL, URI, URN
**URL** (Uniform Resource Locator) – унифицированный указатель на ресурс. Бывает абсолютным и относительным.
> Основным объектом манипуляции в HTTP является ресурс, на который указывает URL в запросе клиента. Список всех доступных ресурсов сайта образуют дерево, в котором каждая ветка будет являться адресом URL.
>
> Ресурсы представляют из себя либо реальные файлы на сервере, например: HTML, js, png, …, либо логические сущности, которые генерируют содержимое на лету
URL является подмножеством более широкого термина **URI** (Uniform Resource Identifier), который обобщает вид различных идентификаторов.
**URN** (Uniform Resource Name) - идентифицирует путь до ресурса (другими словамм единообразное имя ресурса, без указания его местонахождения). Например, книжные международные индексы можно считать URN (мы точно можем определить что это за книга, но не знаем на какой полке она находится)
**URI = URL + URN** // если представить как множества

## HTTP-коды ответов сервера
1xx: Informational (информационные):
2xx: Success (успешно):
3xx: Redirection (перенаправление):
4xx: Client Error (ошибка клиента):
5xx: Server Error (ошибка сервера):
## Уведомление пользователя о событиях
Самый простой способ получать новую информацию от сервера – **периодический опрос**. (**polling**)
Это работает, но есть и недостатки:
1. Сообщения передаются с задержкой между запросами.
2. Даже если сообщений нет, сервер «атакуется» запросами каждые n секунд, даже если пользователь переключился куда-нибудь или спит. С точки зрения производительности, это довольно большая нагрузка.
**«Длинные опросы»** – гораздо лучший способ взаимодействия с сервером. (**long polling**)
Они также очень просты в реализации, и сообщения доставляются без задержек.
Как это происходит:
1. Запрос отправляется на сервер.
1. Сервер не закрывает соединение, пока у него не возникнет сообщение для отсылки.
1. Когда появляется сообщение – сервер отвечает на запрос, посылая его.
1. Браузер немедленно делает новый запрос.
Для данного метода ситуация, когда браузер отправил запрос и удерживает соединение с сервером, ожидании ответа, является стандартной. Соединение прерывается только доставкой сообщений.

Однонаправленность протокола не позволяет серверу посылать запросы клиентам (только отвечать на запросы). Именно поэтому мы прибегаем к pooling'ам.
## WebSocket
Протокол WebSocket обеспечивает возможность обмена данными между браузером и сервером через постоянное соединение. Данные передаются по нему в обоих направлениях (двунаправленный протокол) в виде «пакетов», без разрыва соединения и дополнительных HTTP-запросов.
WebSocket — протокол связи поверх TCP-соединения. Он устанавливает одно соединение и передает ответ на единственный запрос в тот момент, когда ответ появился — без дополнительных запросов, как у HTTP-протокола.
Запросы и ответы приходят без задержек и сетевой нагрузки.
WebSocket особенно хорош для сервисов, которые нуждаются в постоянном обмене данными, например онлайн игры, торговые площадки, работающие в реальном времени, и т.д.
Чтобы открыть веб-сокет-соединение, нам нужно создать объект new WebSocket, указав в url-адресе специальный протокол ws:
```
let socket = new WebSocket("ws://javascript.info");
```
Также существует протокол wss://, использующий шифрование. Это как HTTPS для веб-сокетов.
> Протокол wss:// не только использует шифрование, но и обладает повышенной надёжностью.
>
> Это потому, что данные ws:// не зашифрованы, видны для любого посредника. Старые прокси-серверы не знают о WebSocket, они могут увидеть «странные» заголовки и закрыть соединение.
Как только объект WebSocket создан, мы должны слушать его события. Их всего 4:
* open – соединение установлено,
* message – получены данные,
* error – ошибка,
* close – соединение закрыто.
…А если мы хотим отправить что-нибудь, то вызов socket.send(data) сделает это.
**Открытие веб-сокета**
Когда new WebSocket(url) создан, он тут же сам начинает устанавливать соединение.
Браузер, при помощи специальных заголовков, спрашивает сервер: «Ты поддерживаешь Websocket?» и если сервер отвечает «да», они начинают работать по протоколу WebSocket, который уже не является HTTP.
Вот пример заголовков для запроса, который делает `new WebSocket("wss://javascript.info/chat")`.
```
GET /chat
Host: javascript.info
Origin: https://javascript.info
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q==
Sec-WebSocket-Version: 13
```
* *Origin* – источник текущей страницы (например https://javascript.info). Объект WebSocket по своей природе не завязан на текущий источник. Нет никаких специальных заголовков или других ограничений. Старые сервера все равно не могут работать с WebSocket, поэтому проблем с совместимостью нет. Но заголовок Origin важен, так как он позволяет серверу решать, использовать ли WebSocket с этим сайтом.
* *Connection*: Upgrade – сигнализирует, что клиент хотел бы изменить протокол.
* *Upgrade*: websocket – запрошен протокол «websocket».
* *Sec-WebSocket-Key* – случайный ключ, созданный браузером для обеспечения безопасности.
* *Sec-WebSocket-Version* – версия протокола WebSocket, текущая версия 13.
> **Запрос WebSocket нельзя эмулировать**
> Мы не можем использоватьXMLHttpRequest или fetch для создания такого HTTP-запроса, потому что JavaScript не позволяет устанавливать такие заголовки.
Если сервер согласен переключиться на WebSocket, то он должен отправить в ответ код 101:
```
101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g=
```
WebSocket сам по себе не содержит такие функции, как переподключение при обрыве соединения, аутентификацию пользователей и другие механизмы высокого уровня. Для этого есть клиентские и серверные библиотеки, а также можно реализовать это вручную.
Методы:
* socket.send(data),
* socket.close([code], [reason]).
## Server Sent Events
Спецификация Server-Sent Events описывает встроенный класс EventSource, который позволяет поддерживать соединение с сервером и получать от него события.
Как и в случае с WebSocket, соединение постоянно.
Но есть несколько важных различий:

Основная причина использования SSE -- он проще. Многим приложениям не требуется вся мощь WebSocket.
Чтобы начать получать данные, нам нужно просто создать new EventSource(url).
Браузер установит соединение с url и будет поддерживать его открытым, ожидая события.
Сервер должен ответить со статусом 200 и заголовком Content-Type: text/event-stream, затем он должен поддерживать соединение открытым и отправлять сообщения в особом формате:
```
data: Сообщение 1
id: 1
data: Сообщение 2
id: 2
data: Сообщение 3
data: в две строки
id: 3
```
Для каждого сообщения генерируется событие message.
После создания new EventSource подключается к серверу и, если соединение обрывается, – переподключается.
> После того как соединение окончательно закрыто, «переоткрыть» его уже нельзя. Если необходимо снова подключиться, просто создать новый EventSource.
Когда соединение прерывается из-за проблем с сетью, ни сервер, ни клиент не могут быть уверены в том, какие сообщения были доставлены, а какие – нет.
Чтобы правильно возобновить подключение, каждое сообщение должно иметь поле id. (как в примере выше). С помощью EventSource.lastEventId мы можем получить id последнего полученного сообщения.
По умолчанию объект EventSource генерирует 3 события:
* message – получено сообщение, доступно как event.data.
* open – соединение открыто.
* error – не удалось установить соединение, например, сервер вернул статус 500.
Сервер может указать другой тип события с помощью event: ... в начале сообщения:
```
event: join
data: Боб
```
Чтобы начать слушать пользовательские события, нужно использовать addEventListener, а не onmessage.
Из методов на js только close(), т.к. мы можем только читать данные.
---
## Небольшой ликбез по операторам на JS
Оператор **await** используется для ожидания окончания Promise. Может быть использован только внутри async function.
После вызова функция **async** возвращает Promise.
Интерфейс **Promise** (промис) представляет собой обёртку для значения, неизвестного на момент создания промиса. Он позволяет обрабатывать результаты асинхронных операций так, как если бы они были синхронными: вместо конечного результата асинхронного метода возвращается своего рода обещание (дословный перевод слова "промис") получить результат в некоторый момент в будущем.
Promise может находиться в трёх состояниях:
* ожидание (pending): начальное состояние, не исполнен и не отклонён.
* исполнено (fulfilled): операция завершена успешно.
* отклонено (rejected): операция завершена с ошибкой.
Для получения тела ответа нам нужно использовать дополнительный вызов метода.
Response предоставляет несколько методов, основанных на промисах, для доступа к телу ответа в различных форматах:
* response.text() – читает ответ и возвращает как обычный текст,
* response.json() – декодирует ответ в формате JSON,
* response.formData() – возвращает ответ как объект FormData (разберём его в следующей главе),
* response.blob() – возвращает объект как Blob (бинарные данные с типом),
* response.arrayBuffer() – возвращает ответ как ArrayBuffer (низкоуровневое представление бинарных данных),
* помимо этого, response.body – это объект ReadableStream, с помощью которого можно считывать тело запроса по частям. Мы рассмотрим и такой пример несколько позже.
>Мы можем выбрать только один метод чтения ответа.
---
## Cookies
Значение document.cookie состоит из пар ключ=значение, разделённых ;. Каждая пара представляет собой отдельное куки.
Запись в document.cookie обновит только упомянутые в ней куки, но при этом не затронет все остальные.
Например, этот вызов установит куки с именем user и значением John:
```
document.cookie = "user=John"; // обновляем только куки с именем 'user'
alert(document.cookie); // показываем все куки
```
По умолчанию куки доступно лишь тому домену, который его установил. Так что куки, которые были установлены сайтом site.com, не будут доступны на сайте other.com.
…Но что более интересно, мы не сможем получить эти куки на поддомене forum.site.com!
Нет способа сделать куки доступным на другом домене 2-го уровня, так что other.com никогда не получит куки, установленное сайтом site.com.
Это ограничение безопасности, чтобы мы могли хранить в куки конфиденциальные данные, предназначенные только для одного сайта.
…Однако, если мы всё же хотим дать поддоменам типа forum.site.com доступ к куки, это можно сделать. Достаточно при установке куки на сайте site.com в качестве значения опции domain указать корневой домен: domain=site.com:
## fetch()
JavaScript может отправлять сетевые запросы на сервер и подгружать новую информацию. Например, для этого можно использовать fetch.
Типичный запрос с помощью fetch состоит из двух операторов await:
```
let response = await fetch(url, options); // завершается с заголовками ответа
let result = await response.json(); // читать тело ответа в формате JSON
```
Или, без await:
```
fetch(url, options)
.then(response => response.json())
.then(result => /* обрабатываем результат */)
```
**Параметры ответа:**
* response.status – HTTP-код ответа,
* response.ok – true, если статус ответа в диапазоне 200-299.
* response.headers – похожий на Map объект с HTTP-заголовками.
Методы для получения тела ответа:
* response.text() – возвращает ответ как обычный текст,
* response.json() – преобразовывает ответ в JSON-объект,
* response.formData() – возвращает ответ как объект FormData (кодировка form/multipart, см. следующую главу),
* response.blob() – возвращает объект как Blob (бинарные данные с типом),
* response.arrayBuffer() – возвращает ответ как ArrayBuffer (низкоуровневые бинарные данные),
Опции fetch, которые мы изучили на данный момент:
* method – HTTP-метод,
* headers – объект с запрашиваемыми заголовками (не все заголовки разрешены),
* body – данные для отправки (тело запроса) в виде текста, FormData, BufferSource, Blob или UrlSearchParams.
## XMLHttpRequest vs fetch
В общем они направлены на выполнение одной задачи. Fetch является просто новым методом.
Использование XMLHttpRequest оправдано тем, что он раньше использовался повсеместно, когда не было fetch и теперь нужно как-то поддерживать старый код.
* Помимо этого, XMLHttpRequest способен отслеживать прогресс отправки данных на сервер.
В fetch мы можем выбрать отправлять нам файлы cookies или нет, по умолчанию они не отправляются.
Так же в fetch у нас есть no-cors mode который позволяет разрешить использование только простые запросы на другой источник.
Опция mode – это защита от нечаянной отправки запроса на другой источник.
Эта опция может пригодиться, если URL-адрес для fetch приходит от третьей стороны, и нам нужен своего рода «глобальный выключатель» для запросов на другие источники.
> Запросы no-cors нужны, чтобы получать ответ от сервера, который не реализует CORS.
>
> * "cors" – стоит по умолчанию, позволяет делать такие запросы так, как описано в Fetch: запросы на другие сайты,
> * "same-origin" – запросы на другой источник запрещены,
> * "no-cors" – разрешены только простые запросы на другой источник.
> Простыми считаются запросы, если они удовлетворяют двум условиям:
>
> Простой метод: GET, POST или HEAD
> Простые заголовки – только из определенного списка:
> * Accept
> * Accept-Language
> * Content-Language
> * Content-Type со значением application/x-www-form-urlencoded, multipart/form-data или text/plain.
> Принципиальное отличие между ними (простыми и сложными запросами) состоит в том, что «простой запрос» может быть сделан через `<form>` или `<script>`, без каких-то специальных методов.
Таким образом, даже очень старый сервер должен быть способен принять простой запрос.
Если кто-то (какой-то serviceWorker) перехватит наш запрос, то он не сможет добавить или изменить заголовки, кроме простых. (Сделано это для предотвращения утечки данных)
> (Также, для JavaScript может быть ограничен доступ к любому свойству объекта ответа [Response](https://developer.mozilla.org/ru/docs/Web/API/Response )
> По умолчанию скрипт может прочитать из ответа только «простые» заголовки. Для прочтения других заголовков они должны быть указаны в Access-Control-Expose-Headers.
Соответсвенно, эти штуки не умеет XmlHTTPRequest.
> Еще другие режимы fetch [тут](https://developer.mozilla.org/ru/docs/Web/API/Request/mode#%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D1%8F)
Более безопасные запросы с кукисами [тут](https://learn.javascript.ru/xhr-crossdomain#zaprosy-ot-imeni-polzovatelya)
**mode: no-cors делает две вещи:**
* Он говорит, что мне не нужно видеть результат
* и поэтому он не отправляет предполетный чек
> И соответсвенно может быть полезен при след ситуации:
> В любое время я хочу отправить данные на сервер как можно быстрее, не отправляя предполетную проверку (тк не корс запросы могут быть сложными). Я всегда могу отправить GET с CORS позже, чтобы прочитать данные, когда они мне действительно понадобятся.
## CORS
Cross-origin resource sharing (CORS; с англ. — «совместное использование ресурсов между разными источниками») — технология современных браузеров, **которая позволяет предоставить веб-страницам доступ к ресурсам другого домена.**
Раньеше скрипт с одного сайта не мог получить доступ к содержимому другого сайта.
Для этого использовались разные костыли.
**CORS для простых запросов**
При запросе на другой источник браузер всегда ставит «от себя» заголовок Origin.
Например, если мы запрашиваем. https://anywhere.com/request со страницы https://javascript.info/page, заголовки будут такими:
```
GET /request
Host: anywhere.com
Origin: https://javascript.info
...
```
Как вы можете видеть, заголовок Origin содержит именно источник (домен/протокол/порт), без пути.
Сервер может проверить Origin и, если он согласен принять такой запрос, добавить особый заголовок Access-Control-Allow-Origin к ответу. Этот заголовок должен содержать разрешённый источник (в нашем случае https://javascript.info) или звёздочку *. Тогда ответ успешен, в противном случае возникает ошибка.
Здесь браузер играет роль доверенного посредника:
Он гарантирует, что к запросу на другой источник добавляется правильный заголовок Origin.
Он проверяет наличие разрешающего заголовка Access-Control-Allow-Origin в ответе и, если всё хорошо, то JavaScript получает доступ к ответу сервера, в противном случае – доступ запрещается с ошибкой.
Вот пример ответа сервера, который разрешает доступ:
```
200 OK
Content-Type:text/html; charset=UTF-8
Access-Control-Allow-Origin: https://javascript.info
```
## Как кодируется URL, если в нем есть символы не из латиницы.
Кодировка проходит в 2 этапа.
Сначала каждый символ кодируется 2 символами в кодировке UTF-8, затем каждый символ UTF-8 записывается в 16-ричном виде.
> Стандарт URL использует ASCII
## Режимы работы PHP | CGI FastCGI phpmod PHP-FPM
А. Запуск в режиме загрузки модуля. Фактически, PHP интегрирован в сервер Apache и работает в том же процессе.
б) Запуск в режиме CGI. CGI называется интерфейсом открытого шлюза на английском языке, то есть, когда Apache встречает скрипт PHP, он передает программу PHP в приложение CGI (php-cgi.exe) для интерпретации, а результат после интерпретации возвращается в Apache. Затем возвращается к соответствующему запрашивающему пользователю.
Каждый запрос обрабатывается в отдельном процессе.
c. FastCGI похож на долгоживущий CGI. Эта форма представляет собой расширенную версию CGI. CGI - однопроцессный, многопоточный режим работы, который будет уничтожен после завершения выполнения программы, поэтому каждый раз, когда нужно загрузить переменные конфигурации и среды fork-and-execute (create-execute). FastCGI отличается от других. Его можно запускать постоянно. Пока он активирован, на каждый раз вилка не займет времени. Диспетчер процессов FastCGI инициализирует себя, запускает несколько процессов интерпретатора CGI (несколько файлов php-cgi.exe можно увидеть в диспетчере задач) и ожидает подключения с веб-сервера.
Каждый запрос обрабатывается в отдельном процессе или потоке в зависимости от того, как мы напишем.
d. PHP-FPM
FastCGI Process Manager, "Менеджер процессов FastCGI". Это альтернативная реализация FastCGI режима в PHP с несколькими дополнительными возможностями, которые обычно используются для высоконагруженных сайтов.
## Apache & NginX (веб-сервер)
**Apache** был разработан для доставки веб-контента, доступ к которому осуществляется через Интернет. Очень широко использовался до появления NginX. Модель «один сервер делает все» стала ключом к раннему успеху Apache и последующей проблемой Apache.
Apache гораздо проще своего конкурента. Разрабатывать и обновлять приложения на Apache очень просто. Модель «одно соединение на процесс» позволяет очень легко вставлять модули в любой точке логики веб-обслуживания. Разработчики могут добавлять код таким образом, что в случае сбоев будет затронут только рабочий процесс, выполняющий код. Обработка всех других соединений будет продолжаться без помех.
**Nginx** был разработан специально для устранения ограничений производительности веб-серверов Apache.
Nginx состоит из master-процесса и нескольких дочерних процессов. Мастер процесс обычно один — он создает дочерние процессы (воркеры, загрузчик кеша и кеш менеджер), считывает конфигурацию и открывает порты. Эти дочерние процессы буду обслуживать все соединения с клиентами в неблокирующей манере. В nginx используется бесконечный цикл, который бежит по всем соединениями и отвечает на запросы клиентов. Когда соединение закрывается, оно удаляется из event loop.
Благодаря этому NginX гораздо производительнее чем Apache и способен обрабатывать большее кол-во подключений.
> Также есть различие в контенте, который они могут генерировать:
> * Apache — может генерировать как статический контент, так и динамический.
> * Nginx — отдает только статику и из коробки генерировать динамический контент не умеет. Если вы используете nginx и хотите генерировать динамический контент на своем сайте, то вам придется проксировать запросы тому, кто это делать умеет (apache, php-fpm и др.).
> > Динамический контент -- тот, который меняется в зависимости от характеристик пользователя.
> > Статический -- неизменный.
## this in JS
Объекты в JS представляют набор пар ключ-значение. При этом мы можем создавать методы для этих объектов.
Например:
```
let user = {
name: "Джон",
age: 30
};
user.sayHi = function() {
alert("Привет!");
};
user.sayHi();
```
> Мы могли бы заранее объявить функцию и использовать её в качестве метода.
**Ключевое слово this в методах**
* В глобальном контексте выполнения (за пределами каких-либо функций) this ссылается на глобальный объект вне зависимости от режима (строгий или нестрогий).
* В пределах функции значение this зависит от того, каким образом вызвана функция.
* Для доступа к информации внутри объекта метод может использовать ключевое слово this.
Например:
`sayHi() {
// this - это "текущий объект"
alert(this.name);
}`
* Значение this вычисляется во время выполнения кода и зависит от контекста.
* Стрелочные функции особенные: у них нет своего «собственного» this. Если мы используем this внутри стрелочной функции, то его значение берётся из внешней «нормальной» функции.
* Вызов this без объекта (this) вернет undefined.
**Метод bind**
ECMAScript 5 представил Function.prototype.bind(). Вызов f.bind(someObject) создаёт новую функцию с тем же телом и областью действия, что и f, но там, где в исходной функции используется this, в новой функции оно постоянно будет связано с первым аргументом bind, независимо от того, как функция используется.
**Call и Apply**
Используются для явного указания this.
Вызов func.call(context, a, b...) – то же, что обычный вызов func(a, b...), но с явно указанным this(=context). После контекста в call можно передать аргументы для функции.
Вызов функции при помощи func.apply работает аналогично func.call, но принимает массив аргументов вместо списка.
> При помощи call можно легко взять метод одного объекта, в том числе встроенного, и вызвать в контексте другого.
>
> Это называется «одалживание метода»
## Что происходит, когда пользователь набирает в браузере адрес сайта.
1. Пользователь вводит в браузере адрес сайта.
2. Браузер начинает искать сервер.
3. Браузер отправляет запрос к DNS-серверам.
4. После получения адреса (IP) браузер устанавливает соединение с сервером.
5. Браузер отправляет HTTP-запрос, чтобы получить контент сайта
6. Сервер обрабатывает запрос
7. Сервер отправляет ответ браузеру
8. Браузер обрабатывает полученный ответ и «рисует» веб-страницу
## Keep alive & Connect
HTTP keep-alive или повторное использование соединений HTTP — использование одного TCP-соединения для отправки и получения многократных HTTP-запросов и ответов вместо открытия нового соединения для каждой пары запрос-ответ.
**Связь получается односторонней**.
HTTP CONNECT method запускает двустороннюю связь с запрошенным ресурсом.
## PHP
PHP -- скриптовый язык, часто используемый для написания веб-приложений
PHP -- язык с динамической типизацией, при объявлении переменных их тип не указывается.
> Интерпретатор выполняет код находящийся только внутри ограничителей <? php ... ?> и оставляет неизменным весь оставшийся файл.
Оптимально PHP использовать для небольших проектов, которые нужно быстро сделать.
## Суперглобальные массивы PHP
Суперглобальные массивы -- массивы, которые предопределены интерпретатором PHP заранее. И эти суперглобальные массивы содержат информацию (переменные), которая, предполагается, может понадобиться на любой странице.
Список суперглобальных массивов:
* $_GLOBALS -- массив всех глобальных переменных
* $_SERVER -- параметры, которые ОС передает серверу при его запуске
* $_ENV -- переменные среды ОС
* $_FILES -- сведения об отпправленных методом POST файлах
* $_COOKIE -- содержит массив cookies
* $_REQUEST -- содержит элементы из массивов $_GET, $_POST, $_COOKIE и $_FILES
* $_SESSION -- данные HTTP-сессии
$_GET и $_POST - это фактически параметры запроса. Когда PHP интерпретатор обрабатывает HTTP-запрос, все параметры этого запроса будут автоматически странслированы в эти суперглобальные массивы.
В PHP5+ имеется полная поддержка ООП.
Так же в PHP имеются трейты -- инструмент для повторного использования кода. Объявляем кусок кода trait x {} и потом используем где нам надо use x; в месте где мы напишем use будет инлайн того блока кода, который объявлен в x. (синтаксический сахар)