Upgrade to Pro — share decks privately, control downloads, hide ads and more …

HTTPリクエストの行方 - Laravelがレスポンスを返すまで

HTTPリクエストの行方 - Laravelがレスポンスを返すまで

Webアプリケーションはどうして動くのでしょうか

ブラウザ等のクライアントから送信されたHTTPリクエストを契機に、
Webアプリケーションでは様々なレイヤーでの情報処理が行われます

今回はその情報処理の流れをLaravelを例に追うことで、
Webサーバー、Laravelアプリケーション、データーベースと
レイヤー毎で行われている処理と、そのレイヤー毎のつなぎ込み部分に迫ります。

HTTPヘッダーを含むHTTPリクエストの流れを追って、真にWebアプリケーションがどうして動くのかを知り、
必要な処理をどこに組み込むことが効果的なのかを知る一助になれば幸いです。

shiro seike

August 27, 2022
Tweet

More Decks by shiro seike

Other Decks in Programming

Transcript

  1. HTTPリクエストの行方
    - Laravelがレスポンスを返すまで -

    PHPカンファレンス沖縄2022

    2022.8.27

    清家史郎

    1


    View full-size slide

  2. 自己紹介

    清家 史郎

    @seike460

    - ID

    - GitHub:seike460

    - Twitter:@seike460

    - Work at

    - 株式会社 Fusic (フュージック) 

    技術開発本部/技術開発第一部門 

    - チームリーダー/プリンシパルエンジニア/

    エバンジェリスト

    - Skill

    - PHP/Go/AWS

    - Community

    - Fukuoka.php Organizer 

    - PHPカンファレンス沖縄2019 - 2022 

    PHPカンファレンス2018 - 2022 
 2


    View full-size slide

  3. Agenda

    3

    1. HTTPとは

    2. ClientからのHTTP Request

    3. Requestを受け取るWeb Server

    4. Requestを処理するPHPの世界

    5. PHPとDBのやり取り

    6. ResponseをClientに返却

    7. まとめ


    View full-size slide

  4. 01
    HTTPとは


    View full-size slide

  5. HTTP

    5

    Hypertext Transfer Protocol (HTTP) は HTML などのハイパーメディア文書を

    転送するためのアプリケーション層プロトコル

    ウェブブラウザー(クライアント)とウェブサーバー間の通信を目的として設計

    ※他の用途でも使用されることが有り


    HTTP は旧来のクライアント・サーバーモデルに則っており、クライアントはサーバーに

    リクエストを送信するためにポートを開き、サーバー側からのレスポンスが返ってくるまで待機。


    HTTP ヘッダーによって、プロトコルの拡張や実験が容易になっており
    、新しい機能であっても、

    クライアントとサーバーが新たなヘッダーの意味について単純な合意があれば導入可能。


    HTTP はいわゆるステートレスプロトコルであり、

    サーバーは二つのリクエスト間で何もデータを保持しない。

    参考:MDN Web Docs「HTTP」: https://developer.mozilla.org/ja/docs/Web/HTTP


    View full-size slide

  6. HTTP

    6

    クライアントとサーバーは、個々のメッセージを交換することによって通信。

    キャッシュ、フィルタリング、認証、負荷分散等の機能を提供するプロキシを中継することもある。

    リクエスト…クライアントが送信するメッセージ

    レスポンス…サーバーが回答として送信するメッセージ

    参考:MDN Web Docs「HTTP」: https://developer.mozilla.org/ja/docs/Web/HTTP


    View full-size slide

  7. PHPWebアプリケーションはどうやってHTTPで動く?

    7

    ブラウザ等のHTTPクライアントからHTTPプロトコルを介して

    HTTPメッセージのやり取りを行います。


    View full-size slide

  8. HTTP メッセージ

    8

    HTTPメッセージは図中央の様な決まった形式のテキストを送信することで、

    様々な情報を付与してサーバーに情報を要求して、

    サーバーは要求に応じて処理を変更する事が出来る。


    View full-size slide

  9. HTTPリクエストとHTTPレスポンス

    9

    ブラウザ等のクライアントからHTTPプロトコルを介して

    サーバーとHTTPメッセージのやり取りを行います。

    クライアントが情報を要求してサーバーにアクションを起こさせる

    →HTTPリクエスト

    クライアントの要求に対するサーバーの回答

    →HTTPレスポンス


    View full-size slide

  10. 02
    Clientからの

    HTTP Request


    View full-size slide

  11. HTTP Client

    11

    Guzzle、cURL、wgetなどHTTP Serverに対して情報を要求して、

    情報の提供を受けるソフトウェア等のこと

    ClientからHTTPの仕様に沿ったRequestを送信して情報を要求し、

    ServerがResponseを構築して情報を返答する。


    View full-size slide

  12. HTTP のフロー

    12

    1.TCPコネクションを開く(3ウェイ・ハンドシェイク)

    2.HTTP メッセージを送信する(HTTPリクエスト)

    3.サーバーから送信されたレスポンスを読み取る(HTTPレスポンス)

    4.次のリクエストのために、コネクションを閉じるか再使用する

    ※HTTP/1.1


    View full-size slide

  13. httpbin.org

    13

    送信したHTTP リクエストメッセージを

    返還してくれるサーバーアプリケーション


    HTTPクライアントから送信している

    HTTPリクエストメッセージデバッグに重宝


    OSSとしてGitHubに公開されている為、

    任意の環境にサーバーを立てる事も可能


    https://github.com/postmanlabs/httpbin


    View full-size slide

  14. 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) 


    View full-size slide

  15. HTTP ヘッダー

    15

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


    View full-size slide

  16. 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 

    〜省略〜


    View full-size slide

  17. 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等の場合はリクエスト内にボディが挿入されることがある。 


    View full-size slide

  18. ブラウザによるHTTP

    18


    View full-size slide

  19. Google Chrome 開発者ツール

    19


    View full-size slide

  20. 一般的な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をつけることでキャッシュをさせなくしたり、

    有効期限を設定したりします。


    View full-size slide

  21. 拡張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で、リクエストの行方を追うために利用する 


    View full-size slide

  22. Sessionが利用するHTTPリクエストヘッダー

    22

    Cookie: "hoge=XxxxxxxxxxxxxxxxxxxxxxxxxxX" 


    サーバーが情報処理後、状態を維持するのに必要な情報をサーバーに保存して、セッションIDを発行 

    レスポンスヘッダーのSet-Cookie ヘッダーでセッションIDを送信 

    保存されたCookieからセッションIDをリクエストヘッダーに付与する事で、最初に保存した状態の情報をやり取りする 


    詳しくは、PHPerKaigi2021 / PHPで学ぶ Session の基本と応用 / web-app-session-101 @hanhan1978


    View full-size slide

  23. 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 

    〜省略〜


    View full-size slide

  24. 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 

    <


    View full-size slide

  25. 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 

    〜省略〜


    View full-size slide

  26. 03
    Requestを受け取る

    Web Server


    View full-size slide

  27. Webサーバー

    27

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

    View full-size slide

  28. 静的コンテンツ

    28

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

    View full-size slide

  29. WebサーバーとPHP

    29

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

    View full-size slide

  30. 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;

    View full-size slide

  31. Webサーバーを使う理由

    31

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

    View full-size slide

  32. 04
    Requestを処理する

    PHPの世界


    View full-size slide

  33. Webサーバー -> PHP

    33

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

    View full-size slide

  34. 定義済み変数

    34

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

    View full-size slide

  35. 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 


    View full-size slide

  36. Laravel public/index.php

    36

    $_SERVERに入った「HTTP_*」や

    getallheaders()から得られるArrayから

    HTTPヘッダーの情報を得られます


    同じ様に$_GETや$_POSTを利用することで

    HTTPリクエストの情報をPHPにて取得可能


    一方スーパーグローバル変数をそのまま利用するのは

    利用できるスコープの観点からもアンチパターン


    では実際はどの様に処理しているのかを

    Laravelを例に見てみます


    View full-size slide

  37. public/index.php

    37


    View full-size slide

  38. Http/Request.php

    38

    SymfonyRequest::createFromGlobals()を元にRequestインスタンスを作っていそう

    View full-size slide

  39. symfony/http-foundation/Request.php

    39

    createRequestFromFactoryに先程の定義済み変数を投げている

    View full-size slide

  40. symfony/http-foundation/Request.php - 2

    40

    定義済み変数を利用して、 Static インスタンスを生成している

    View full-size slide

  41. symfony/http-foundation/Request.php - 3

    41

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

    View full-size slide

  42. 05
    PHPとDBのやり取り


    View full-size slide

  43. PHP -> Database

    43

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

    View full-size slide

  44. Database/Eloquent/Model.php

    44


    View full-size slide

  45. Database/Eloquent/Model.php - 2

    45


    View full-size slide

  46. Database/Eloquent/Model.php - 3

    46


    View full-size slide

  47. Database/Connectors/ConnectionFactory.php

    47


    View full-size slide

  48. Database/Connectors/Connector.php

    48


    View full-size slide

  49. MySQLの場合はpdo-mysql

    50


    View full-size slide

  50. Database/Connectors/MySqlConnector.php

    51

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

    View full-size slide

  51. 06
    ResponseをClientに返却


    View full-size slide

  52. Foundation/Http/Kernel.php

    53


    View full-size slide

  53. Request

    54


    View full-size slide

  54. Routing/Router.php

    55


    View full-size slide

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

    56


    View full-size slide

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

    57

    ContentsType、
    Content-Length等の
    ResponseHeaderも含め
    構築を行う
    構築が行われた
    ResponseがWebサーバーへ
    返される

    View full-size slide

  57. HTTP レスポンス

    58

    PHPで生成されたレスポンスメッセージをNginx側に返します

    NginxはPHPから受け取ったレスポンスメッセージと、

    自らが返信すべきレスポンスヘッダーを付与して

    HTTPクライアントへ返却を行います。


    Google Chrome 開発者ツールが受けた

    レスポンスヘッダーの例が右のとおりです。

    View full-size slide

  58. 07
    まとめ


    View full-size slide

  59. まとめ

    Point 2

    HTTPメッセージのやり取りを理解して、Webアプリケーションを理解する

    60

    PHPは一般的にHTTPメッセージであるHTTPリクエストを契機にHTTPレスポンスを構築する

    Point 1


    各レイヤーの結合部は近代技術による抽象化により意識せずに開発が出来る

    Point 3

    Point 4

    どんな技術にも基礎はある。基礎技術を理解して正しいHTTPアプリケーション構築を行いましょう


    View full-size slide

  60. ご清聴いただきありがとうございました

    Thank You
    We are Hiring !

    https://recruit.fusic.co.jp/


    View full-size slide