Введение в веб

Сеть снизу вверх: адреса и маршруты, TCP и UDP, DNS, HTTP и TLS, клиент и сервис на Python

Вячеслав Федоров

Лекция 12

Разработка и применение ПО

в физических исследованиях

GET

Содержание

От провода до собственного веб-сервиса

01

Слои и адреса

02

Маршрутизация

03

Транспорт

04

DNS, HTTP и TLS

05

Клиент на Python

06

Свой сервис

Цель лекции

Понимать путь запроса от программы до сервера и находить место, где он оборвался

Каждый уровень сети решает свою задачу и проверяется своей командой. Тот, кто представляет путь запроса целиком, отличает отсутствие маршрута от неразрешённого имени, закрытый порт от ошибки сервера и медленный канал от медленной программы — и знает, как получить данные из чужого сервиса и как открыть доступ к своим.

Путь

одного запроса

import requests
 
r = requests.get(
    'https://api.example.org/runs',
    timeout=5)
print(r.status_code, r.json())

Пояснение

Одна строка кода запускает пять шагов на разных уровнях сети.

ШагПротоколЧем проверить
имя → адресDNSdig, curl к DoH
адрес → маршрутIPip route get, tracepath
соединение с портомTCPss, tcpdump
шифрованиеTLSopenssl, curl -v
запрос и ответHTTPcurl -v, requests

Слои и адреса

Модели OSI и TCP/IP, инкапсуляция, интерфейсы, MAC, IPv4 и IPv6

01

Слои и адреса

Маршрутизация

Транспорт

DNS, HTTP и TLS

Клиент на Python

Свой сервис

Сеть

слоями

Уровни OSIУровень TCP/IPПротоколыЕдиница данных
7–5 прикладной, представления, сеансовыйприкладнойHTTP, DNS, TLS, SSHсообщение
4 транспортныйтранспортныйTCP, UDP, QUICсегмент, датаграмма
3 сетевоймежсетевойIP, ICMPпакет
2–1 канальный, физическийсетевого доступаEthernet, Wi-Fiкадр

Пояснение

Слой пользуется услугами нижнего и не знает его устройства: Wi-Fi появился без переписывания браузеров.

Для физика

Диагностику начинают с самой простой проверки: интерфейс, имя, маршрут, порт, ответ.

Инкапсуляция

заголовков

Ethernet

IP

TCP

HTTP: GET /runs …

FCS

данные приложения

сегмент TCP: порты, номера, флаги

пакет IP: адреса отправителя и получателя, TTL

кадр Ethernet: MAC-адреса соседей и контрольная сумма FCS

Получатель

снимает заголовки
в обратном порядке

Маршрутизатор

меняет кадр,
уменьшает TTL,
порты сохраняет

Адреса

IPv4 и IPv6

ДиапазонНазначение
10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16частные сети (RFC 1918)
127.0.0.0/8, ::1петлевой интерфейс, сама машина
169.254.0.0/16, fe80::/10локальные адреса канала
192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24документация (RFC 5737)
2001:db8::/32документация IPv6 (RFC 3849)

>>> import ipaddress
>>> net = ipaddress.ip_network('192.0.2.0/24')
>>> net.num_addresses, net.netmask
(256, IPv4Address('255.255.255.0'))

01

IPv4

32 бита, четыре октета: 192.0.2.10

02

Префикс /24

24 старших бита — номер сети, 8 — номер узла

03

IPv6

128 бит; 2001:db8:0:0:0:0:0:1 = 2001:db8::1

Для физика

Адреса учебных сетей лекции взяты из диапазонов документации.

Канальный уровень:

MAC и ARP

$ ip -br link
va@if2  UP  f2:82:ac:e0:e0:79 <BROADCAST,UP,…>
 
$ ip neigh
192.0.2.20 dev va lladdr f2:80:58:c1:92:87 REACHABLE

Пояснение

Программа знает IP-адрес соседа, а кадр отправляется на MAC-адрес: соответствие выясняет ARP.

01

MAC-адрес

48 бит; у сетевой карты задан производителем, у виртуальных интерфейсов случаен

02

ARP

широковещательный вопрос «у кого 192.0.2.20?» и ответ с MAC

03

IPv6

вместо ARP — обнаружение соседей NDP

04

VLAN

логические сети на одном оборудовании

Демонстрация:

сеть из двух узлов

01

Пространства имён a и b и пара veth между ними

02

Интерфейсы и адреса узла a

03

ping соседа и таблица ARP

Что наблюдается

В пространстве a только петлевой lo и конец пары va; после ping в таблице соседей появляется MAC-адрес узла b.

root@lab — запись со стенда

Маршрутизация

Таблица маршрутов, самый конкретный префикс, правила, диагностика пути

02

Слои и адреса

Маршрутизация

Транспорт

DNS, HTTP и TLS

Клиент на Python

Свой сервис

Таблица

маршрутов

$ ip route
default via 192.0.2.1 dev va
192.0.2.0/24 dev va proto kernel scope link
198.51.100.128/25 via 192.0.2.254 dev va
 
$ ip route get 198.51.100.200
198.51.100.200 via 192.0.2.254 dev va

Важно

Сервер соседнего корпуса не отвечает, а машины своей комнаты доступны: чаще всего нет маршрута до сети.

01

Самый конкретный

из подходящих строк выбирается префикс наибольшей длины

02

default

0.0.0.0/0 — всё, что не подошло под другие строки

03

ip route get

спрашивает ядро, какой маршрут оно выберет

Задача:

выбор маршрута

default        via 192.0.2.1
64.0.0.0/4     via 192.0.2.2
128.0.0.0/1    via 192.0.2.19
192.0.2.0/25   via 192.0.2.9
192.0.2.48     via 192.0.2.17

Пояснение

Префикс /n задаёт число старших битов, которые должны совпасть с адресом.

АдресВыбранная строкаПочему
192.0.2.48192.0.2.48 via 192.0.2.17/32 — точный адрес
192.0.2.100192.0.2.0/25 via 192.0.2.9/25 конкретнее /1 и default
200.1.2.3128.0.0.0/1 via 192.0.2.19/1 конкретнее /0
10.1.1.1default via 192.0.2.1ничего конкретнее

Несколько

таблиц и правила

$ ip rule
0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup default
 
# ответы сети установки — через её шлюз
ip route add default via 10.20.0.1 table 100
ip rule add from 10.20.0.0/16 lookup 100

01

Правила

по порядку приоритетов выбирают таблицу для пакета

02

Признаки

адрес отправителя, интерфейс, метка пакета

03

Зачем

машина в двух сетях отвечает каждой через её шлюз

Для физика

Компьютер на установке: институтская сеть и сеть управления на разных интерфейсах.

Диагностика

пути

КомандаЧто показывает
pingдоступность и время туда и обратно
tracepath, tracerouteузлы пути по истечению TTL
mtrping и traceroute непрерывно, потери на каждом узле
ip route getвыбор маршрута самим ядром

01

TTL

каждый маршрутизатор уменьшает его на 1; на нуле пакет отбрасывается

02

MTU

наибольший размер пакета на участке; tracepath находит его по пути

03

Потери

mtr показывает, на каком узле они начинаются

Демонстрация:

маршрутизатор

01

Сети 192.0.2.0/24 и 198.51.100.0/24, узел r между ними

02

ping без маршрута: сеть недоступна

03

Маршрут по умолчанию через r: ping и tracepath

04

Более конкретный /25 и ip route get

Что наблюдается

Без маршрута — Network is unreachable; через r ответ приходит с ttl=63, а /25 перехватывает адреса .128–.255.

root@lab — запись со стенда

Транспорт

Порты и сокеты, UDP и TCP, рукопожатие, контроль перегрузок, tcpdump

03

Слои и адреса

Маршрутизация

Транспорт

DNS, HTTP и TLS

Клиент на Python

Свой сервис

Порты

и сокеты

$ ss -tln
State   Local Address:Port   Peer Address:Port
LISTEN  127.0.0.1:8000        0.0.0.0:*
LISTEN  0.0.0.0:22            0.0.0.0:*

ПортСлужба
22SSH
53DNS
80, 443HTTP, HTTPS
5432PostgreSQL

01

Порт

16-битный номер программы на машине

02

Соединение

четвёрка: адрес и порт с каждой стороны

03

Порты ниже 1024

привилегированные: по умолчанию занять их может только root

04

Временные порты

клиенту ядро выдаёт номер из 32768–60999

UDP

и

TCP

СвойствоUDPTCP
соединениенетрукопожатие перед данными
доставкабез гарантийподтверждения и повторы
порядокне сохраняетсясохраняется
единицадатаграммапоток байтов
применениеDNS, видео, QUICHTTP/1.1–2, SSH, почта

Пояснение

UDP: для DNS проще повторить запрос, чем поддерживать соединение; потерянный кадр видеосвязи уже не нужен.

Пояснение

HTTP/3 работает поверх QUIC на UDP: надёжность реализована над UDP заново.

Для физика

О потере пакетов телеметрии UDP не сообщает: пропуски выявляет счётчик в данных.

Рукопожатие

TCP

клиент

сервер :8000

SYN, seq x

SYN-ACK, seq y, ack x+1

ACK — соединение установлено

GET / HTTP/1.1 … (данные, флаг P)

HTTP/1.0 200 OK … (данные)

FIN — сервер закрывает соединение

Демонстрация:

рукопожатие в tcpdump

01

http.server на 127.0.0.1:8000 и ss

02

tcpdump на петлевом интерфейсе

03

curl и разбор перехваченных сегментов

Что наблюдается

Флаги [S], [S.], [.] — рукопожатие; [P.] — запрос 77 байт и ответ; [F.] первым отправляет сервер HTTP/1.0.

root@lab — запись со стенда

Контроль

перегрузок

Медленный старт

окно растёт вдвое
за каждый RTT до порога

Reno, CUBIC

потеря пакета —
признак перегрузки:
окно резко уменьшается

BBR

модель канала:
измеряет полосу и RTT,
потеря — не признак перегрузки

Оценка скорости TCP Reno при доле потерянных пакетов p (формула Матиса):

скорость ≈ MSS / RTT × 1,22 / √p → 1448 байт / 50 мс × 1,22 / 0,1 ≈ 350 КБ/с

Linux по умолчанию использует CUBIC; BBR включается параметром net.ipv4.tcp_congestion_control.

Демонстрация:

CUBIC и BBR при потерях

01

Сеть a—r—b: на r задержка 50 мс и 1 % потерь (tc netem)

02

Сервер в b с файлом 8 МиБ, RTT по ping

03

Скачивание трижды при CUBIC

04

BBR у отправителя и снова трижды

Что наблюдается

CUBIC: 23,0–25,4 с на файл, как по формуле Матиса; BBR: 0,90–0,99 с — примерно в 25 раз быстрее.

root@lab — запись со стенда

Перехват

пакетов

tcpdump -i any -nn 'tcp port 443'
tcpdump -i eth0 -nn host 192.0.2.20
tcpdump -i lo -w dump.pcap 'port 8000'
tcpdump -r dump.pcap -nn -A
ss -tnp            # чьи соединения

Пояснение

Файл .pcap открывает Wireshark: графики окна, повторы, задержки.

01

-i

интерфейс; any — все сразу

02

-nn

без разрешения имён и портов

03

фильтр

выражение BPF: host, port, tcp, udp

04

-w и -r

запись в файл и чтение

Важно

Перехват на чужой сети без разрешения недопустим: пакеты содержат чужие данные.

DNS, HTTP и TLS

Имена и записи, URL, запрос и ответ, коды состояния, сертификаты

04

Слои и адреса

Маршрутизация

Транспорт

DNS, HTTP и TLS

Клиент на Python

Свой сервис

DNS:

имена в адреса

программа

рекурсивный резолвер

корень .

зона com

example.com

куда спрашивать про com

куда про example.com

A 104.20.23.154

кеш на время TTL

ЗаписьСодержитПример
A, AAAAадрес IPv4, IPv6example.com → 104.20.23.154
CNAMEпсевдоним другого имениwww.github.com → github.com
MXпочтовые серверы с приоритетомgmail.com → 5 gmail-smtp-in…
TXTтекст: проверка владения, SPF«v=spf1 …»

Демонстрация:

DNS поверх HTTPS

01

Скрипт doh.sh: запрос к 1.1.1.1 по HTTPS, ответ в JSON

02

Записи A и AAAA для example.com

03

CNAME для www.github.com

04

Почтовые серверы gmail.com

Что наблюдается

www.github.com — псевдоним github.com; у gmail.com пять MX с приоритетами 5–40; второе число в строке — оставшийся TTL в секундах.

root@lab — запись со стенда

URL:

адрес ресурса

https://api.example.org:443/v1/runs?limit=10#top

https

://

api.example.org

:443

/v1/runs

?limit=10

#top

ЧастьКому нужна
httpsспособ взаимодействия: HTTP поверх TLS
api.example.orgDNS: имя превращается в адрес
:443транспорт: порт программы на сервере; по умолчанию 443 или 80
/v1/runs?limit=10сервер: путь и параметры запроса
#topтолько браузер: на сервер не отправляется

Запрос

и ответ

GET /hello.txt HTTP/1.1
Host: localhost
 
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6
Content-type: text/plain
Content-Length: 13
 
pulse 4.2 mA

МетодДействиеБезопасныйИдемпотентный
GETполучить ресурсдада
HEADтолько заголовкидада
POSTсоздать, отправить данныенетнет
PUTзаменить целикомнетда
PATCHизменить частьнетне обязательно
DELETEудалитьнетда

Пояснение

Идемпотентный запрос можно повторить при обрыве: состояние сервера останется тем же.

Коды

состояния

КлассСмыслПримерыЧто делать клиенту
2xxуспех200 OK, 201 Created, 204 No Content—
3xxперенаправление301, 302, 304 Not Modifiedклиент идёт по Location; 304 — копия из кеша
4xxошибка клиента400, 401, 403, 404, 422, 429исправить запрос; 429 — подождать
5xxошибка сервера500, 502, 503, 504повторить позже с паузой

Пояснение

401 — нет аутентификации, 403 — доступ запрещён, 404 — ресурса нет.

Для физика

Ночной сбор данных повторяет запрос только при 5xx и 429, при 4xx — останавливается.

Демонстрация:

HTTP вручную

01

http.server на 127.0.0.1:8000

02

Запрос из файла: строки с CR LF

03

Отправка через nc и ответ сервера

04

curl -v для отсутствующего пути

Что наблюдается

Сервер отвечает HTTP/1.0 200 OK, Content-Length: 13 и телом; несуществующий путь — 404 File not found.

root@lab — запись со стенда

TLS:

буква S в HTTPS

01

Шифрование

посторонние на пути не читают и не меняют данные

02

Подлинность

сертификат доказывает, что сервер — тот, за кого себя выдаёт

03

Цепочка доверия

сертификат подписан центром, которому доверяет клиент

04

TLS 1.3

рукопожатие за один RTT; SSL, TLS 1.0 и 1.1 запрещены

Пояснение

Let's Encrypt выдаёт сертификаты бесплатно и автоматически по протоколу ACME.

Важно

verify=False отключает проверку подлинности: шифрование остаётся, защиты от подмены — нет.

Пояснение

HTTP/2 — много запросов одновременно в одном соединении; HTTP/3 — поверх QUIC на UDP.

Демонстрация:

свой сертификат

01

Сертификат и ключ для localhost на сутки

02

openssl s_server на порту 8443

03

curl без доверия к сертификату

04

curl с --cacert: TLS 1.3

Что наблюдается

Без --cacert curl отказывается с кодом 60: самоподписанного сертификата нет среди доверенных; с ним — TLSv1.3 и ответ 200.

root@lab — запись со стенда

Клиент на Python

Сокет, requests, повторы, API и JSON, скрапинг, асинхронные запросы

05

Слои и адреса

Маршрутизация

Транспорт

DNS, HTTP и TLS

Клиент на Python

Свой сервис

HTTP

через сокет

import socket
 
with socket.create_connection(('127.0.0.1', 8000)) as s:
    s.sendall(b'GET /hello.txt HTTP/1.1\r\n'
              b'Host: localhost\r\n\r\n')
    reply = b''
    while chunk := s.recv(4096):  # до закрытия
        reply += chunk
print(reply.split(b'\r\n\r\n')[1])

Пояснение

Протокол — текст в открытом TCP-соединении; далее необходимо найти конец заголовков, дочитать тело, учесть кодировку и перенаправления.

Для физика

Сокеты нужны для своих протоколов: телеметрия установки, SCPI-команды приборам.

requests

с тайм-аутом и повторами

r = requests.get(url, params={'limit': 10}, timeout=5)
r.raise_for_status()      # 4xx, 5xx — исключение
data = r.json()
 
s = requests.Session()
retry = Retry(total=4, backoff_factor=0.5,
              status_forcelist=[429, 503])
s.mount('https://', HTTPAdapter(max_retries=retry))

01

timeout

без него зависший сервер останавливает программу навсегда

02

raise_for_status

ошибка HTTP становится исключением

03

Session

общие заголовки и переиспользуемое соединение

04

Retry

повтор с растущей паузой; учитывает Retry-After

Демонстрация:

тайм-аут и повтор

01

Сервер flaky.py: два ответа 503 на /data, /slow — через 3 с

02

requests с timeout=1 к /slow

03

Session с Retry к /data

04

Журнал сервера

Что наблюдается

Запрос к /slow прерывается ReadTimeout через 1 с; к /data — 200 с третьей попытки за 1,0 с, в журнале 503, 503, 200.

root@lab — запись со стенда

API

и JSON

import os
 
token = os.environ['ARCHIVE_TOKEN']
s = requests.Session()
s.headers['Authorization'] = f'Bearer {token}'
r = s.get(f'{API}/runs', params={'page': 2},
          timeout=10)

Важно

Ключ доступа — пароль: хранится в переменной окружения, не в репозитории.

01

JSON

объекты и списки, строки, числа, true, false, null

02

NaN

в JSON нет: пропуск кодируют null

03

Квота

429 Too Many Requests — сделать паузу

04

OAuth

токен с ограниченными правами вместо пароля

Скрапинг

страниц

soup = BeautifulSoup(page.content, 'html.parser')
for tr in soup.select('table#magnets tr')[1:]:
    cells = [td.get_text(strip=True)
             for td in tr.find_all('td')]

Важно

Без charset в заголовке requests декодирует text/html как ISO-8859-1: разбирают байты page.content.

01

robots.txt

правила обхода, которые просит соблюдать владелец

02

User-Agent

имя программы и контакт автора

03

Паузы и кеш

не больше запроса в секунду, без повторных загрузок

04

API лучше

вёрстка меняется без предупреждения

Демонстрация:

таблица со страницы

01

Каталог site через http.server

02

robots.txt: разрешённые и запрещённые пути

03

Кодировка ответа и разбор байтов

04

Строки таблицы магнитов

Что наблюдается

/private/ запрещён в robots.txt; без charset requests выбрал ISO-8859-1 — заголовок испорчен, из байтов — «Магнитная система канала».

root@lab — запись со стенда

Асинхронные

запросы

async def main():
    limit = asyncio.Semaphore(10)
    async with httpx.AsyncClient() as client:
        async def one(url):
            async with limit:
                return await client.get(url)
        return await asyncio.gather(*map(one, urls))

Пояснение

Время уходит на ожидание сети: пока один ответ в пути, отправляются другие запросы.

Важно

Тысяча одновременных запросов для чужого сервера неотличима от атаки: параллелизм ограничивают.

Пояснение

httpx по умолчанию ждёт ответа 5 с, requests — без ограничения.

Демонстрация:

асинхронный httpx

01

Сервис slow.py на FastAPI: ответ через 0,2 с

02

20 запросов по очереди через httpx.Client

03

20 запросов асинхронно, не больше 10 одновременно

Что наблюдается

По очереди — 4,13 с, асинхронно — 0,47 с: две волны по 10 запросов вместо двадцати ожиданий подряд.

root@lab — запись со стенда

Свой сервис

REST, FastAPI и проверка данных, документация, nginx перед приложением

06

Слои и адреса

Маршрутизация

Транспорт

DNS, HTTP и TLS

Клиент на Python

Свой сервис

REST:

ресурсы и методы

ЗапросДействиеОтвет
POST /runsсоздать запуск201 Created
GET /runs?min_energy=3список с фильтром200 OK
GET /runs/2один запуск200 или 404
PUT /runs/2заменить целиком200 или 404
DELETE /runs/2удалить204 или 404

Для физика

Журнал запусков установки: коллеги читают его из своих скриптов, а не просят выгрузку.

Пояснение

Ресурс — существительное в пути, действие — метод HTTP, результат — код состояния.

FastAPI

и проверка данных

class Run(BaseModel):
    energy: float = Field(gt=0)    # МэВ
    current: float = Field(ge=0)   # мА
 
@app.post('/runs', status_code=201)
def create(run: Run):
    _, new_id, _ = sql('INSERT INTO runs …',
                       (run.energy, run.current))
    return {'id': new_id, **run.model_dump()}

01

Модель Pydantic

типы и ограничения полей; ошибка — ответ 422

02

Параметры

из пути и строки запроса по аннотациям функции

03

Документация

/docs и /redoc строятся из кода автоматически

04

uvicorn

сервер ASGI: uvicorn app:app --port 8000

Демонстрация:

сервис на FastAPI

01

uvicorn с приложением app.py

02

POST двух запусков, GET с фильтром

03

DELETE и GET удалённого запуска

04

Некорректные данные и журнал запросов

Что наблюдается

201 с id, фильтр min_energy=3 оставляет один запуск, DELETE — 204, затем 404; энергия −1 и ток «много» — 422 с двумя ошибками.

root@lab — запись со стенда

Результат:

документация /docs

Интерактивная документация Swagger UI сервиса «Журнал запусков» с пятью операциями

Что наблюдается

Пять операций и схема Run построены из кода; кнопки Try it out и Execute отправляют запрос прямо из браузера.

nginx

перед приложением

клиент

nginx :8080

/api/ → uvicorn :8000

/ → статика

location /api/ {
    proxy_pass http://127.0.0.1:8000/;
    proxy_set_header X-Forwarded-For $remote_addr;
}

Пояснение

nginx завершает TLS, раздаёт статику, ограничивает частоту, распределяет нагрузку между копиями.

Демонстрация:

nginx в контейнере

01

uvicorn с приложением на :8000

02

nginx.conf и запуск контейнера nginx:alpine

03

Запросы через :8080: статика и /api/

04

Заголовки и журнал nginx

Что наблюдается

Ответ /api/runs приходит от приложения, но с заголовком Server: nginx/1.31.6; в журнале nginx — все три запроса.

root@lab — запись со стенда

Итоги

лекции

01

Сеть устроена слоями: каждый проверяется своей командой — ip, ss, tcpdump, dig, curl.

02

Маршрут — самый конкретный подходящий префикс; ip route get показывает выбор ядра.

03

TCP надёжен ценой рукопожатия и контроля перегрузок; при случайных потерях скорость сохраняет BBR.

04

HTTP — текст: метод, путь, заголовки, код состояния; TLS добавляет шифрование и подлинность.

05

Клиент — requests или httpx с тайм-аутом и повтором; свой сервис — FastAPI за nginx.

Упражнения

и задание

01

Задача о маршрутах

таблица со слайда в пространстве имён: проверить ответы через ip route get

02

Свой tcpdump

перехватить рукопожатие HTTPS-запроса и найти в нём ClientHello

03

Сбор данных

клиент к публичному API с тайм-аутом, повтором и паузой между запросами

04

Журнал запусков

добавить к сервису PATCH и фильтр по току, проверить через /docs

Пояснение

Задание курса: «Деплой стартапа „Котики в мир“» — nginx перед приложением в Docker.

Для физика

Поиск неисправности в сети начинается с проверки имени, маршрута и порта.

Источники

1

Книга курса: главы «Компьютерные сети», «Веб-технологии», «Работа с базами данных»

phys-dev.github.io/soft-dev-book

2

Лекция «Как починить Интернет», КИТ 2024, Young&&Yandex; Б. Литочкин

youtube.com/watch?v=ScSfNwxBU7A

3

Kurose J., Ross K. Computer Networking: A Top-Down Approach. – 9th ed. – Pearson, 2026

gaia.cs.umass.edu/kurose_ross

4

MDN Web Docs: HTTP — методы, заголовки, коды состояния

developer.mozilla.org/ru/docs/Web/HTTP

5

Cardwell N. et al. BBR: Congestion-Based Congestion Control. – ACM Queue, 2016. – Vol. 14, No. 5

doi.org/10.1145/3012426.3022184

6

Mathis M. et al. The Macroscopic Behavior of the TCP Congestion Avoidance Algorithm. – ACM SIGCOMM CCR, 1997. – Vol. 27, No. 3

doi.org/10.1145/263932.264023

7

Документация requests, httpx и FastAPI

requests.readthedocs.io, python-httpx.org, fastapi.tiangolo.com

8

RFC 9110 HTTP Semantics; RFC 8446 TLS 1.3; RFC 9293 TCP

rfc-editor.org/rfc/rfc9110

Спасибо за внимание

Вопросы

phys-dev.github.io/soft-dev-book

200