特定のHTTPステータスコードを発生させる
HTTPステータスコードってありますよね。
普通にインターネットしてるだけでも「404 Not Found」、「500 Internal Server Error」あたりはけっこうな頻度で目にします。
障害調査なんかで、その時は発生していたけど後から気づいたときに治っていたりして、どうやって再現させようと困ることが多いので、今回は「このコードなんだっけ」というときに意図的に特定のステータスコードを再現させてみたいと思います。
※4xx系と5xx系のエラーコードをメインに試します。
詳しい解説などはWikipediaでどうぞ…
環境を準備する
今回はAWSでELBとEC2を準備し、EC2(ubuntu)にNginxとApache、使うかわかりませんがphpを入れたいと思います。
既に手持ちのサーバーなどがある場合は読み飛ばしてください。
EC2作成
EC2をローンチしておきます。
@local PC
ssh接続
$ ssh -iubuntu@<サーバーのIP>
@EC2
各種インストール
$ sudo apt-get update $ sudo apt-get -y upgrade $ sudo apt-get -y install nginx-extras $ sudo apt-get -y install apache2 $ sudo apt-get -y install php7.0 $ sudo apt-get -y install libapache2-mod-php
Apache設定
$ sudo vi /etc/apache2/apache2.confOptions Indexes FollowSymLinks - AllowOverride None + AllowOverride All Require all granted $ sudo vi /etc/apache2/ports.conf -Listen 80 +Listen 8080 $ sudo vi /etc/apache2/sites-available/000-default.conf - + $ sudo vi /etc/apache2/mods-available/dir.conf - DirectoryIndex index.html index.cgi index.pl index.php index.xhtml index.htm + DirectoryIndex index.php $ sudo service apache2 start
起動確認
$ sudo netstat -anp | egrep "nginx|apache2" | grep ^tcp tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 13441/nginx -g daem tcp6 0 0 :::8080 :::* LISTEN 14800/apache2 tcp6 0 0 :::80 :::* LISTEN 13441/nginx -g daem
phpinfo作成
$ sudo vi /var/www/html/index.php
Nginx設定
$ sudo vi /etc/nginx/sites-available/default location / { - try_files $uri $uri/ =404; + proxy_pass http://localhost:8080/; }
http://EC2のIP or DNS名/ で phpinfoページが表示されればOKです。
ここまできたらELBも作成し、80->80のリスナーを作成しておきます。
やってみる
さっそく試してみます。
再現が難しかったり、手間がかかりそうなステータスコードは省略します。。
curlやtelnetを実行するのはクライアントとなるPC上です。コードごとに入れた設定は次のコードの時にはクリアした状態です。
400 Bad Request
リクエストが不正である。定義されていないメソッドを使うなど、クライアントのリクエストがおかしい場合に返される。
おかしいメソッドでリクエストしてみます。
$ curl http://hoge.elb.amazonaws.com/ -X 1234 -I HTTP/1.1 400 Bad Request Content-Type: text/html Date: Xxx, 01 Jan 2017 00:00:00 GMT Server: nginx/1.10.0 (Ubuntu) Content-Length: 182 Connection: keep-alive
400が返ってきました。
401 Unauthorized
認証が必要である。
Basic認証を設定します。
$ sudo htpasswd -c -b /etc/apache2/.htpasswd user password Adding password for user user $ sudo vi /var/www/html/.htaccess AuthUserFile /etc/apache2/.htpasswd AuthName "Basic Auth" AuthType Basic Require valid-user
リクエストしてみます。
$ curl http://hoge.elb.amazonaws.com/ -I HTTP/1.1 401 Unauthorized Content-Type: text/html; charset=iso-8859-1 Date: Xxx, 01 Jan 2017 00:00:00 GMT Server: nginx/1.10.0 (Ubuntu) WWW-Authenticate: Basic realm="Basic Auth" Connection: keep-alive
401が返ってきました。
403 Forbidden
禁止されている。リソースにアクセスすることを拒否された。
すべてdenyします。
$ sudo vi /var/www/html/.htaccess require all denied
リクエストしてみます。
$ curl http://hoge.elb.amazonaws.com/ -I HTTP/1.1 403 Forbidden Content-Type: text/html; charset=iso-8859-1 Date: Xxx, 01 Jan 2017 00:00:00 GMT Server: nginx/1.10.0 (Ubuntu) Connection: keep-alive
403が返ってきました。
404 Not Found
未検出。リソースが見つからなかった。
存在しないURLにリクエストしてみます。
$ curl http://hoge.elb.amazonaws.com/404 -I HTTP/1.1 404 Not Found Content-Type: text/html; charset=iso-8859-1 Date: Xxx, 01 Jan 2017 00:00:00 GMT Server: nginx/1.10.0 (Ubuntu) Connection: keep-alive
404が返ってきました。
405 Method Not Allowed
許可されていないメソッド。
許可されていなさそうなメソッドでリクエストしてみます。
$ curl http://hoge.elb.amazonaws.com/ -X TRACE -I HTTP/1.1 405 Not Allowed Content-Type: text/html Date: Xxx, 01 Jan 2017 00:00:00 GMT Server: nginx/1.10.0 (Ubuntu) Content-Length: 182 Connection: keep-alive
405が返ってきました。
406 Not Acceptable
受理できない。Accept関連のヘッダに受理できない内容が含まれている場合に返される。
これはちょっと手間がかかる予感。
サーバー側の判定次第なような気もするので、406を返すようにApacheを設定します。
$ sudo apt-get install libapache2-mod-security2 $ sudo cp -p /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf $ sudo vi /etc/modsecurity/modsecurity.conf + SecRule REQUEST_URI "406" "id:'999999',log,deny,status:406" $ service apache2 restart
設定したURIにリクエストしてみます。
$ curl http://hoge.elb.amazonaws.com/406 -I HTTP/1.1 406 Not Acceptable Content-Type: text/html; charset=iso-8859-1 Date: Xxx, 01 Jan 2017 00:00:00 GMT Server: nginx/1.10.0 (Ubuntu) Connection: keep-alive
406が返ってきました。
408 Request Timeout
リクエストタイムアウト。リクエストが時間以内に完了していない場合に返される。
curlだと厳しそうなので、telnetで接続して放置してみます。
$ telnet hoge.elb.amazonaws.com 80 Trying xxx.xxx.xxx.xxx... Connected to hoge.elb.amazonaws.com. Escape character is '^]'. GET / HTTP/1.1 <-ここで放置 HTTP/1.1 408 REQUEST_TIMEOUT Content-Length:0 Connection: Close
408が返ってきました。
410 Gone
消滅した。リソースは恒久的に移動・消滅した。どこに行ったかもわからない。
これは404で返すサーバーのほうが多そうです。。
mod_rewriteで無理やり返してみます。
$ sudo ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/ $ sudo service apache2 reload $ sudo vi /var/www/html/.htaccess RewriteEngine On RewriteRule ^index\.php$ - [G]
設定したコンテンツにリクエストしてみます。
$ curl http://hoge.elb.amazonaws.com/index.php -I HTTP/1.1 410 Gone Content-Type: text/html; charset=iso-8859-1 Date: Xxx, 01 Jan 2017 00:00:00 GMT Server: nginx/1.10.0 (Ubuntu) Connection: keep-alive
410が返ってきました。
413 Payload Too Large
ペイロードが大きすぎる。リクエストエンティティがサーバの許容範囲を超えている場合に返す。
大きなContent-Lengthヘッダを突っ込んでリクエストしてみました。
$ curl http://hoge.elb.amazonaws.com/ -H "Content-Length: 10000000" -I HTTP/1.1 413 Request Entity Too Large Content-Length: 208 Content-Type: text/html Date: Xxx, 01 Jan 2017 00:00:00 GMT Server: nginx/1.10.0 (Ubuntu) Connection: Close
413が返ってきました。
414 URI Too Long
URIが大きすぎる。URIが長過ぎるのでサーバが処理を拒否した場合に返す。
8192バイト以上で発生するというような情報を見かけたのでそれ以上の長いURIでリクエストしてみました。
$ curl http://hoge.elb.amazonaws.com/?parameter=(略) -I HTTP/1.1 414 Request-URI Too Large Content-Length: 202 Content-Type: text/html Date: Xxx, 01 Jan 2017 00:00:00 GMT Server: nginx/1.10.0 (Ubuntu) Connection: keep-alive
414が返ってきました。
417 Expectation Failed
レンジは範囲外にある。実リソースのサイズを超えるデータを要求した。具体例として、Expect:ヘッダに100-continue以外の変なものを入れた場合や、そもそもサーバが100 Continueを扱えない場合に返す。
具体例に従ってリクエストしてみます。
$ curl http://hoge.elb.amazonaws.com/ -H "Expect: 001-continue" -X POST -v * Trying xxx.xxx.xxx.xxx... * TCP_NODELAY set * Connected to hoge.elb.amazonaws.com (xxx.xxx.xxx.xxx) port 80 (#0) > POST / HTTP/1.1 > Host: hoge.elb.amazonaws.com > User-Agent: curl/7.51.0 > Accept: */* > Expect: 001-continue > < HTTP/1.1 417 EXPECTATION_FAILED < Content-Length: 0 < Connection: Close < * Curl_http_done: called premature == 0 * Closing connection 0
417が返ってきました。
500 Internal Server Error
サーバ内部エラー。サーバ内部にエラーが発生した場合に返される。
誤った設定を入れました。
$ sudo vi /var/www/html/.htaccess 適当な文字列(例えば「500」とか)
リクエストしてみます。
$ curl http://hoge.elb.amazonaws.com/ -I HTTP/1.1 500 Internal Server Error Content-Type: text/html; charset=iso-8859-1 Date: Xxx, 01 Jan 2017 00:00:00 GMT Server: nginx/1.10.0 (Ubuntu) Connection: keep-alive
500が返ってきました。
502 Bad Gateway
不正なゲートウェイ。ゲートウェイ・プロキシサーバは不正な要求を受け取り、これを拒否した。
Apacheを止めます。
$ sudo service apache2 stop
リクエストしてみます。
$ curl http://hoge.elb.amazonaws.com/ -I HTTP/1.1 502 Bad Gateway Content-Length: 182 Content-Type: text/html Date: Xxx, 01 Jan 2017 00:00:00 GMT Server: nginx/1.10.0 (Ubuntu) Connection: keep-alive
502が返ってきました。
※Apacheの領域にELB用のヘルスチェックコンテンツを置いている場合は、502の後、OutOfServiceで503になります。
503 Service Unavailable
サービス利用不可。サービスが一時的に過負荷やメンテナンスで使用不可能である。
ELBのヘルスチェックコンテンツをリネームしてヘルスチェックに失敗させます。
$ sudo mv /var/www/html/index.html /var/www/html/index.html_
EC2のインスタンスステータスがOutOfServiceになったらリクエストしてみます。
$ curl http://hoge.elb.amazonaws.com/ -I HTTP/1.1 503 Service Unavailable: Back-end server is at capacity Connection: keep-alive
503が返ってきました。
504 Gateway Timeout
ゲートウェイタイムアウト。ゲートウェイ・プロキシサーバはURIから推測されるサーバからの適切なレスポンスがなくタイムアウトした。
これは例えばバックエンドのアプリケーションで処理が止まってレスポンスが返ってこない状態で、ELBのアイドルタイムアウトに達したときなどに起こりえます。
サーバー側の環境にもよりますが、今回は以下の内容でリクエストしてみます。
$ curl http://hoge.elb.amazonaws.com/ -H "Content-Length: 01" -I HTTP/1.1 504 GATEWAY_TIMEOUT Connection: keep-alive
504が返ってきました。
まとめ
たったこれだけ再現させるのも探り探りだったのでまあまあメンドくさかったです。
ステータスコードの解説はあっても、意外に実際どうやったら起きるのかは少なかったので書いてみました。