PHPでAWS SDKを使い、ファイルをアップロードしたら417 Expectation Failedが起きた時の対策

  • 0
    Like
  • 0
    Comment

    なかなか解決しなかった上、全体的に情報が少ない現象だったので、備忘録として現象と対策を残しておこうと思う。

    まず、構成について、下記のような状態です
    [EC2:PHPアプリサーバ]->[EC2:Squid3プロキシ]->[S3]

    • PHPアプリサーバ
      PHP 5.6.17
      Apache 2.2.27

    • Squid3プロキシ
      Squid 3.1.23

    Kernel.sysctl.conf
    net.ipv4.ip_forward = 0
    net.ipv4.conf.default.rp_filter = 1
    net.ipv4.conf.default.accept_source_route = 0
    kernel.sysrq = 0
    kernel.core_uses_pid = 1
    net.ipv4.tcp_syncookies = 1
    net.bridge.bridge-nf-call-ip6tables = 0
    net.bridge.bridge-nf-call-iptables = 0
    net.bridge.bridge-nf-call-arptables = 0
    kernel.msgmnb = 65536
    kernel.msgmax = 65536
    kernel.shmmax = 68719476736
    kernel.shmall = 4294967296
    kernel.panic = 5
    net.ipv4.tcp_tw_recycle = 1
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.ip_local_port_range = 10240    65535
    fs.inotify.max_user_watches = 1677721
    
    squid.conf
    acl manager proto cache_object
    acl localhost src 127.0.0.1/32 ::1
    acl to_localhost dst 127.0.0.0/8 0.0.0.0/32 ::1
    acl localnet src 10.0.0.0/8     # RFC1918 possible internal network
    acl localnet src 172.16.0.0/12  # RFC1918 possible internal network
    acl localnet src 192.168.0.0/16 # RFC1918 possible internal network
    acl localnet src fc00::/7       # RFC 4193 local private network range
    acl localnet src fe80::/10      # RFC 4291 link-local (directly plugged) machines
    acl SSL_ports port 443
    acl Safe_ports port 80          # http
    acl Safe_ports port 21          # ftp
    acl Safe_ports port 443         # https
    acl Safe_ports port 70          # gopher
    acl Safe_ports port 210         # wais
    acl Safe_ports port 1025-65535  # unregistered ports
    acl Safe_ports port 280         # http-mgmt
    acl Safe_ports port 488         # gss-http
    acl Safe_ports port 591         # filemaker
    acl Safe_ports port 777         # multiling http
    acl CONNECT method CONNECT
    http_access allow manager localhost
    http_access deny manager
    http_access deny !Safe_ports
    http_access deny CONNECT !SSL_ports
    http_access allow localnet
    http_access allow localhost
    http_access allow all
    icp_access allow all
    icp_port 0
    http_access deny all
    http_port 3128
    http_port 3151 transparent
    http_port 3152 intercept
    coredump_dir /var/spool/squid
    refresh_pattern ^ftp:           1440    20%     10080
    refresh_pattern ^gopher:        1440    0%      1440
    refresh_pattern -i (/cgi-bin/|\?) 0     0%      0
    refresh_pattern .               0       20%     4320
    visible_hostname unkown
    forwarded_for off
    request_header_access X-FORWARDED-FOR deny all
    request_header_access Via deny all
    request_header_access Cache-Control deny all
    max_filedesc 262144
    
    

    以上の設定で、[PHPアプリサーバ]から[Squid3プロキシ]を通して[S3]のバケットにデータをS3 Client::putObjectを実行した時、

    AWS HTTP error: cURL error 56: Received HTTP code 417 from proxy after CONNECT (see
     http://curl.haxx.se/libcurl/c/libcurl-errors.html)  (client): 417 Expectation Failed
    

    というエラーを出し、アップロードが失敗する現象が発生した。
    問題なのは、ファイルによって出たり出なかったりする点、必ず出るなら対策しやすいと思うが、出る場合、出ない場合、ファイルサイズによっても挙動が変わったりと、現象が安定しない。
    PHPやcURL、ライブラリの調整などいろいろ行ってみたが効果がなく、同様の現象を探したところ、

    https://stackoverflow.com/questions/13670040/amazon-s3-putobject-not-working-php

    I've had this issue behind a squid3 proxy and the problem was resolved by the following squid configuration directive:

    ignore_expect_100 on
    

    という情報を得ることが出来た、この設定はSquid Proxyの設定に対して設定するモノで、デフォルトはoff、ファイルの拡張リクエストを許可するかどうかというものらしい。S3ライブラリでは、一定のサイズになると、ヘッダーにexpect_100を追加して、ファイル情報の拡張を行うようなのだが、Squidを通してS3にコマンドが送られると、このヘッダーが理解されないので、応答が返らなくなってしまうというのが、417のエラーの正体のようだ。

    そこで、squid.confに対して、"ignore_expect_100 on"を定義することで現象が解決することが確認できた。
    修正したsquid.confは以下の通り

    squid.conf
    acl manager proto cache_object
    acl localhost src 127.0.0.1/32 ::1
    acl to_localhost dst 127.0.0.0/8 0.0.0.0/32 ::1
    acl localnet src 10.0.0.0/8     # RFC1918 possible internal network
    acl localnet src 172.16.0.0/12  # RFC1918 possible internal network
    acl localnet src 192.168.0.0/16 # RFC1918 possible internal network
    acl localnet src fc00::/7       # RFC 4193 local private network range
    acl localnet src fe80::/10      # RFC 4291 link-local (directly plugged) machines
    acl SSL_ports port 443
    acl Safe_ports port 80          # http
    acl Safe_ports port 21          # ftp
    acl Safe_ports port 443         # https
    acl Safe_ports port 70          # gopher
    acl Safe_ports port 210         # wais
    acl Safe_ports port 1025-65535  # unregistered ports
    acl Safe_ports port 280         # http-mgmt
    acl Safe_ports port 488         # gss-http
    acl Safe_ports port 591         # filemaker
    acl Safe_ports port 777         # multiling http
    acl CONNECT method CONNECT
    http_access allow manager localhost
    http_access deny manager
    http_access deny !Safe_ports
    http_access deny CONNECT !SSL_ports
    http_access allow localnet
    http_access allow localhost
    http_access allow all
    icp_access allow all
    icp_port 0
    http_access deny all
    http_port 3128
    http_port 3151 transparent
    http_port 3152 intercept
    coredump_dir /var/spool/squid
    refresh_pattern ^ftp:           1440    20%     10080
    refresh_pattern ^gopher:        1440    0%      1440
    refresh_pattern -i (/cgi-bin/|\?) 0     0%      0
    refresh_pattern .               0       20%     4320
    visible_hostname unkown
    forwarded_for off
    ignore_expect_100 on
    request_header_access X-FORWARDED-FOR deny all
    request_header_access Via deny all
    request_header_access Cache-Control deny all
    max_filedesc 262144
    

    設定後、./squid reloadを実施して、再度、S3 Client::putObjectを実行した結果、問題なくファイルがアップロードされた。
    同じ問題に直面している人は少ないと思うが、ここに情報を残しておこうと思う。

    誰かの助けになれば幸いだ。