Slide 1

Slide 1 text

HTTPリクエストの行方 - Laravelがレスポンスを返すまで -
 PHPカンファレンス沖縄2022
 2022.8.27
 清家史郎
 1


Slide 2

Slide 2 text

自己紹介
 清家 史郎
 @seike460
 - ID
 - GitHub:seike460
 - Twitter:@seike460
 - Work at
 - 株式会社 Fusic (フュージック) 
 技術開発本部/技術開発第一部門 
 - チームリーダー/プリンシパルエンジニア/ 
 エバンジェリスト
 - Skill
 - PHP/Go/AWS
 - Community
 - Fukuoka.php Organizer 
 - PHPカンファレンス沖縄2019 - 2022 
 PHPカンファレンス2018 - 2022 
 2


Slide 3

Slide 3 text

Agenda
 3
 1. HTTPとは
 2. ClientからのHTTP Request
 3. Requestを受け取るWeb Server
 4. Requestを処理するPHPの世界
 5. PHPとDBのやり取り
 6. ResponseをClientに返却
 7. まとめ


Slide 4

Slide 4 text

01 HTTPとは


Slide 5

Slide 5 text

HTTP
 5
 Hypertext Transfer Protocol (HTTP) は HTML などのハイパーメディア文書を 
 転送するためのアプリケーション層プロトコル 
 ウェブブラウザー(クライアント)とウェブサーバー間の通信を目的として設計 
 ※他の用途でも使用されることが有り 
 
 HTTP は旧来のクライアント・サーバーモデルに則っており、クライアントはサーバーに 
 リクエストを送信するためにポートを開き、サーバー側からのレスポンスが返ってくるまで待機。 
 
 HTTP ヘッダーによって、プロトコルの拡張や実験が容易になっており 、新しい機能であっても、
 クライアントとサーバーが新たなヘッダーの意味について単純な合意があれば導入可能。 
 
 HTTP はいわゆるステートレスプロトコルであり、
 サーバーは二つのリクエスト間で何もデータを保持しない。 
 参考:MDN Web Docs「HTTP」: https://developer.mozilla.org/ja/docs/Web/HTTP


Slide 6

Slide 6 text

HTTP
 6
 クライアントとサーバーは、個々のメッセージを交換することによって通信。 
 キャッシュ、フィルタリング、認証、負荷分散等の機能を提供するプロキシを中継することもある。 
 リクエスト…クライアントが送信するメッセージ 
 レスポンス…サーバーが回答として送信するメッセージ 
 参考:MDN Web Docs「HTTP」: https://developer.mozilla.org/ja/docs/Web/HTTP


Slide 7

Slide 7 text

PHPWebアプリケーションはどうやってHTTPで動く?
 7
 ブラウザ等のHTTPクライアントからHTTPプロトコルを介して
 HTTPメッセージのやり取りを行います。


Slide 8

Slide 8 text

HTTP メッセージ
 8
 HTTPメッセージは図中央の様な決まった形式のテキストを送信することで、
 様々な情報を付与してサーバーに情報を要求して、
 サーバーは要求に応じて処理を変更する事が出来る。


Slide 9

Slide 9 text

HTTPリクエストとHTTPレスポンス
 9
 ブラウザ等のクライアントからHTTPプロトコルを介して
 サーバーとHTTPメッセージのやり取りを行います。
 クライアントが情報を要求してサーバーにアクションを起こさせる
 →HTTPリクエスト
 クライアントの要求に対するサーバーの回答
 →HTTPレスポンス


Slide 10

Slide 10 text

02 Clientからの
 HTTP Request


Slide 11

Slide 11 text

HTTP Client
 11
 Guzzle、cURL、wgetなどHTTP Serverに対して情報を要求して、
 情報の提供を受けるソフトウェア等のこと
 ClientからHTTPの仕様に沿ったRequestを送信して情報を要求し、
 ServerがResponseを構築して情報を返答する。


Slide 12

Slide 12 text

HTTP のフロー
 12
 1.TCPコネクションを開く(3ウェイ・ハンドシェイク)
 2.HTTP メッセージを送信する(HTTPリクエスト)
 3.サーバーから送信されたレスポンスを読み取る(HTTPレスポンス)
 4.次のリクエストのために、コネクションを閉じるか再使用する
 ※HTTP/1.1


Slide 13

Slide 13 text

httpbin.org
 13
 送信したHTTP リクエストメッセージを
 返還してくれるサーバーアプリケーション
 
 HTTPクライアントから送信している
 HTTPリクエストメッセージデバッグに重宝
 
 OSSとしてGitHubに公開されている為、
 任意の環境にサーバーを立てる事も可能
 
 https://github.com/postmanlabs/httpbin


Slide 14

Slide 14 text

telnetによるHTTPリクエスト
 14
 $ telnet www.httpbin.org 80
 Trying 3.94.154.124...
 Connected to www.httpbin.org.
 Escape character is '^]'.
 GET /get HTTP/1.1 ★実際に入力
 Host: httpbin.org ★実際に入力
 
 HTTP/1.1 200 OK
 Date: Wed, 17 Aug 2022 14:25:15 GMT
 Content-Type: application/json
 Content-Length: 197
 Connection: keep-alive
 Server: gunicorn/19.9.0
 Access-Control-Allow-Origin: *
 Access-Control-Allow-Credentials: true
 
 {
 "args": {},
 "headers": {
 "Host": "httpbin.org",
 "X-Amzn-Trace-Id": "Root=1-62fcfa4b-11ed483b37e7ba455a897f3a"
 },
 "origin": "125.56.55.69",
 "url": "http://httpbin.org/get"
 }
 telnetを利用しhttpbin.orgの 
 80番ポートに接続(HTTP標準ポート) 
 
 接続するHTTP Method (GET) 
 接続するURI (/get) 
 接続するHTTP Version(HTTP/1.1) 
 
 Host リクエストヘッダー(httpbin.org) 


Slide 15

Slide 15 text

HTTP ヘッダー
 15
 HTTP リクエストやレスポンスでクライアントやサーバーが追加情報を渡す リクエストヘッダー … 読み込むリソースについての情報や、クライアントに関する詳細な情報 レスポンスヘッダー … レスポンスに関する追加情報(場所や提供しているサーバーに関する情報) エンティティヘッダー … リソースの本体に関する情報(MIMEタイプや適用されるエンコード/圧縮方式などについての情報) ペイロードヘッダー … 転送されるデータの表現から独立した情報(コンテンツ長さや転送エンコード方式) 参考:MDN Web Docs「HTTP」: https://developer.mozilla.org/ja/docs/Web/HTTP


Slide 16

Slide 16 text

curlによるHTTPリクエスト
 16
 $ curl -v --http1.1 http://httpbin.org/get 
 
 * Trying 34.227.213.82:80... 
 * Connected to httpbin.org (34.227.213.82) port 80 (#0) 
 > GET /get HTTP/1.1 
 > Host: httpbin.org
 > User-Agent: curl/7.79.1 
 > Accept: */*
 >
 * Mark bundle as not supporting multiuse 
 < HTTP/1.1 200 OK 
 < Date: Fri, 19 Aug 2022 15:43:37 GMT 
 < Content-Type: application/json 
 < Content-Length: 253 
 < Connection: keep-alive 
 〜省略〜


Slide 17

Slide 17 text

HTTPリクエストの構成要素
 17
 ■リクエストライン
 GET … HTTP メソッド。クライアントが実行したい操作を定義する GET や POST等 
 /get … リソースのパス 
 HTTP/1.1 … HTTP プロトコルのバージョン 
 > GET /get HTTP/1.1 
 
 ■リクエストヘッダー 
 サーバーに追加の情報を与える任意のHTTPヘッダー 
 > Host: httpbin.org
 > User-Agent: curl/7.79.1 
 > Accept: */*
 
 ■空行
 >
 ■メッセージボディ
 POST等の場合はリクエスト内にボディが挿入されることがある。 


Slide 18

Slide 18 text

ブラウザによるHTTP
 18


Slide 19

Slide 19 text

Google Chrome 開発者ツール
 19


Slide 20

Slide 20 text

一般的なHTTPリクエストヘッダー
 20
 Accept: "*/*"
 クライアントが理解できるコンテンツタイプを MIME タイプで伝える
 
 Accept-Encoding: "gzip, deflate, br"
 コンテンツのエンコーディング、圧縮アルゴリズムのどれをクライアントが理解することができるかを示します。
 Apacheのmod_deflate等が利用
 gzip:Lempel-Ziv coding (LZ77) と32ビット CRC を用いた圧縮形式
 deflate:zlib 構造体と deflate 圧縮アルゴリズムを用いた圧縮形式
 br:Brotli アルゴリズムを用いた圧縮形式
 
 Accept-Language: "ja,en-US;q=0.9,en;q=0.8"
 クライアントがどの言語を理解できるか、どの種類のロケールが推奨されるかを示す
 
 Cache-Control: "max-age=0"
 キャッシュをコントロールするヘッダーで、 no-storeをつけることでキャッシュをさせなくしたり、
 有効期限を設定したりします。


Slide 21

Slide 21 text

拡張HTTPリクエストヘッダー
 21
 Sec-Fetch-Dest: "document", 
 Sec-Fetch-Mode: "navigate", 
 Sec-Fetch-Site: "none", 
 Sec-Fetch-User: "?1", 
 フェッチメタデータリクエストヘッダー 
 リクエストの発信元のコンテキストに関する追加情報を提供する HTTP リクエストヘッダー 
 リクエストがどこから来たのかという追加情報をサーバーに提供し、悪意のあるリクエストを無視できるようになる 
 
 Sec-Ch-Ua: "" Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"" 
 Sec-Ch-Ua-Mobile: "?0", 
 Sec-Ch-Ua-Platform: ""macOS"", 
 User-Agent Client Hints 
 ユーザーエージェント(UA)クライアントヒントヘッダにより、サーバーはユーザーエージェント(ブラウザ)、 
 オペレーティングシステム、デバイスに応じて応答を変化させることができる。 
 
 X-Amzn-Trace-Id: "Root=1-61eb64b7-24bc07aa6d8b197a203a12a9" 
 AWSが付与するTrace情報 
 Application Load Balancerが付与するIDで、リクエストの行方を追うために利用する 


Slide 22

Slide 22 text

Sessionが利用するHTTPリクエストヘッダー
 22
 Cookie: "hoge=XxxxxxxxxxxxxxxxxxxxxxxxxxX" 
 
 サーバーが情報処理後、状態を維持するのに必要な情報をサーバーに保存して、セッションIDを発行 
 レスポンスヘッダーのSet-Cookie ヘッダーでセッションIDを送信 
 保存されたCookieからセッションIDをリクエストヘッダーに付与する事で、最初に保存した状態の情報をやり取りする 
 
 詳しくは、PHPerKaigi2021 / PHPで学ぶ Session の基本と応用 / web-app-session-101 @hanhan1978


Slide 23

Slide 23 text

POST リクエスト
 23
 $ curl -v -X POST --http1.1 http://httpbin.org/post 
 * Trying 54.147.68.244:80... 
 * Connected to httpbin.org (54.147.68.244) port 80 (#0) 
 > POST /post HTTP/1.1 
 > Host: httpbin.org
 > User-Agent: curl/7.79.1 
 > Accept: */*
 >
 * Mark bundle as not supporting multiuse 
 < HTTP/1.1 200 OK 
 < Date: Fri, 19 Aug 2022 15:55:10 GMT 
 < Content-Type: application/json 
 < Content-Length: 317 
 〜省略〜


Slide 24

Slide 24 text

POST Bodyの送信
 24
 $ curl -v -H "Content-Type: application/json" -d '{"hoge":"fuga"}' --http1.1 http://httpbin.org/post 
 * Trying 52.87.105.151:80... 
 * Connected to httpbin.org (52.87.105.151) port 80 (#0) 
 > POST /post HTTP/1.1 
 > Host: httpbin.org 
 > User-Agent: curl/7.79.1 
 > Accept: */*
 > Content-Type: application/json 
 > Content-Length: 15 
 >
 * Mark bundle as not supporting multiuse 
 < HTTP/1.1 200 OK 
 < Date: Fri, 19 Aug 2022 15:57:20 GMT 
 < Content-Type: application/json 
 < Content-Length: 426 
 < Connection: keep-alive 
 < Server: gunicorn/19.9.0 
 < Access-Control-Allow-Origin: * 
 < Access-Control-Allow-Credentials: true 
 <


Slide 25

Slide 25 text

POST Bodyの送信
 25
 $ curl -v -H "Content-Type: application/json" -d '{"hoge":"fuga"}' --http1.1 --trace-ascii - http://httpbin.org/post 
 Warning: --trace-ascii overrides an earlier trace/verbose option 
 == Info: Trying 34.227.213.82:80... 
 == Info: Connected to httpbin.org (34.227.213.82) port 80 (#0) 
 => Send header, 132 bytes (0x84) 
 0000: POST /post HTTP/1.1 
 0015: Host: httpbin.org 
 0028: User-Agent: curl/7.79.1 
 0041: Accept: */* 
 004e: Content-Type: application/json 
 006e: Content-Length: 15 
 0082:
 => Send data, 15 bytes (0xf) 
 0000: {"hoge":"fuga"} 
 == Info: Mark bundle as not supporting multiuse 
 〜省略〜


Slide 26

Slide 26 text

03 Requestを受け取る
 Web Server


Slide 27

Slide 27 text

Webサーバー
 27
 HTTP Clientからのリクエストを受け付けてレスポンスを返却する HTTP Server Apache、Nginx 、あるいはPHP自身がWebサーバーになることも可能 ( php -S 0.0.0.0:80 ) 基本的には静的にコンテンツを返し、動的にレスポンスのコンテンツの内容を変更するのは プログラミング言語である PHP等の役目

Slide 28

Slide 28 text

静的コンテンツ
 28
 Webサーバーとして代表的なソフトウェアは Apache、Nginx DocumentRoot(Nginxの場合はroot)に設定されたディレクトリにあるファイルコンテンツを返す HTTPアクセス時にURIが設定されていない場合は Indexに設定されているファイルを返す

Slide 29

Slide 29 text

WebサーバーとPHP
 29
 静的コンテンツを返す Webサーバーが、なぜリクエストに応じた動的コンテンツを返すのかは Webサーバーの後ろに待ち構えているアプリケーション( PHP等)と連携しているから - Apache ModuleとしてPHPを動作させる - ApacheのプロセスとPHPのプロセスが分離していない - php-fpmを利用してFastCGIとしてPHPを動作させる - Webサーバーのプロセスと PHPのプロセスが分離している Nginxは並列性やメモリ効率、静的リソースの取り扱いに長けているので、 用途によっては選択することに意味を持たせることが出来ます

Slide 30

Slide 30 text

php-fpm
 30
 FastCGI Process Manager (FPM) プロセスを落とすことなく CGIを高速化させる仕組み php-fpm(PHP-FastCGI Process Manager)が利用されるのが一般的 ポートでの通信や、Unixドメインソケットを介した通信がある ■ポートを介した設定 [php-fpm] listen = 0.0.0.0:9000 ↓ [Nginx] fastcgi_pass XXX.XXX.XXX.XXX:9000 ■Unixドメインソケットを介した設定(高速だが単独でのサーバー構成限定) [php-fpm] listen = /var/run/php-fpm/php-fpm.sock ↓ [Nginx] fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;

Slide 31

Slide 31 text

Webサーバーを使う理由
 31
 基本的にはWebサーバーでの静的コンテンツの返却の方が、 PHPでの動的コンテンツの返却より高速かつ負荷が低い状態でコンテンツを返すことが可能 また、当たり前だがWebサーバーの機能を利用が出来るのも優位性の一つ ※リバースプロキシやロードバランサー、レスポンス結果のキャッシュ等 Webサーバーでの制御を行う事でサーバーの負荷を下げたり、通信量を減らしたりことが出来る - 例 - limit_except:利用することで受け付ける HTTPメソッドの制限 - Accept-Encoding「gzip」がリクエストヘッダーに飛んできた際に圧縮通信 - gzip on; - gzip_types text/html text/css text/javascript;

Slide 32

Slide 32 text

04 Requestを処理する
 PHPの世界


Slide 33

Slide 33 text

Webサーバー -> PHP
 33
 HTTPクライアントからWebサーバーへ、WebサーバーからPHPへリクエストが送られてきました 次はPHPはどの様に送信された HTTPメッセージを利用するかです PHPの基本的なHTTPメッセージの取り扱い方をおさらいした後に、 LaravelのRequestインスタンスがどの様に生成されるかを見てみます

Slide 34

Slide 34 text

定義済み変数
 34
 $_SERVER $_GET $_POST $_FILES などの定義済み変数 この変数を参照する事で HTTPメッセージの値を 受け取る事が出来る

Slide 35

Slide 35 text

HTTPリクエスト情報をPHPで取得する
 35
 $ php -S localhost:8000 
 [Sat Jan 21 23:01:11 2022] PHP 7.4.27 Development Server (http://localhost:8000) started 
 [Sat Jan 21 23:02:53 2022] [::1]:56677 Accepted 
 [Sat Jan 21 23:02:53 2022] [::1]:56677 [200]: GET / 
 [Sat Jan 21 23:02:53 2022] [::1]:56677 Closing 
 
 $ curl -v --http1.1 http://localhost:8000 


Slide 36

Slide 36 text

Laravel public/index.php
 36
 $_SERVERに入った「HTTP_*」や 
 getallheaders()から得られるArrayから 
 HTTPヘッダーの情報を得られます 
 
 同じ様に$_GETや$_POSTを利用することで 
 HTTPリクエストの情報をPHPにて取得可能 
 
 一方スーパーグローバル変数をそのまま利用するのは 
 利用できるスコープの観点からもアンチパターン 
 
 では実際はどの様に処理しているのかを 
 Laravelを例に見てみます


Slide 37

Slide 37 text

public/index.php
 37


Slide 38

Slide 38 text

Http/Request.php
 38
 SymfonyRequest::createFromGlobals()を元にRequestインスタンスを作っていそう 


Slide 39

Slide 39 text

symfony/http-foundation/Request.php
 39
 createRequestFromFactoryに先程の定義済み変数を投げている

Slide 40

Slide 40 text

symfony/http-foundation/Request.php - 2
 40
 定義済み変数を利用して、 Static インスタンスを生成している

Slide 41

Slide 41 text

symfony/http-foundation/Request.php - 3
 41
 initializeにてHTTPメッセージを 利用したRequestインスタンス生成 こちらの情報が更に加工されて LaravelのController等の Requestインスタンスとして利用

Slide 42

Slide 42 text

05 PHPとDBのやり取り


Slide 43

Slide 43 text

PHP -> Database
 43
 WebサーバーからPHPへリクエストが送られ、そのリクエストを元にレスポンスを構築します 最後にPHPを扱う上で利用することが多い RDBの接続について注目します Laravelは普段このレイヤーを抽象化してくれているので、その実態を追います

Slide 44

Slide 44 text

Database/Eloquent/Model.php
 44


Slide 45

Slide 45 text

Database/Eloquent/Model.php - 2
 45


Slide 46

Slide 46 text

Database/Eloquent/Model.php - 3
 46


Slide 47

Slide 47 text

Database/Connectors/ConnectionFactory.php
 47


Slide 48

Slide 48 text

Database/Connectors/Connector.php
 48


Slide 49

Slide 49 text

PDO
 49


Slide 50

Slide 50 text

MySQLの場合はpdo-mysql
 50


Slide 51

Slide 51 text

Database/Connectors/MySqlConnector.php
 51
 この様にLaravelは普段このレイヤーを抽象化してくれています。 内部的にはPDOを利用した接続等を行っており、 その部分を意識せずに構築出来るのは Laravelのおかげです。 この作成したコネクションに対して statementを生成してSQLを実行しますが、今回は割愛します

Slide 52

Slide 52 text

06 ResponseをClientに返却


Slide 53

Slide 53 text

Foundation/Http/Kernel.php
 53


Slide 54

Slide 54 text

Request
 54


Slide 55

Slide 55 text

Routing/Router.php
 55


Slide 56

Slide 56 text

vendor/symfony/http-foundation/Response.php
 56


Slide 57

Slide 57 text

vendor/symfony/http-foundation/Response.php-2
 57
 ContentsType、 Content-Length等の ResponseHeaderも含め 構築を行う 構築が行われた ResponseがWebサーバーへ 返される

Slide 58

Slide 58 text

HTTP レスポンス
 58
 PHPで生成されたレスポンスメッセージをNginx側に返します 
 NginxはPHPから受け取ったレスポンスメッセージと、 
 自らが返信すべきレスポンスヘッダーを付与して 
 HTTPクライアントへ返却を行います。 
 
 Google Chrome 開発者ツールが受けた 
 レスポンスヘッダーの例が右のとおりです。 


Slide 59

Slide 59 text

07 まとめ


Slide 60

Slide 60 text

まとめ
 Point 2
 HTTPメッセージのやり取りを理解して、Webアプリケーションを理解する 
 60
 PHPは一般的にHTTPメッセージであるHTTPリクエストを契機にHTTPレスポンスを構築する
 Point 1
 
 各レイヤーの結合部は近代技術による抽象化により意識せずに開発が出来る
 Point 3
 Point 4
 どんな技術にも基礎はある。基礎技術を理解して正しいHTTPアプリケーション構築を行いましょう


Slide 61

Slide 61 text

ご清聴いただきありがとうございました
 Thank You We are Hiring !
 https://recruit.fusic.co.jp/