読者です 読者をやめる 読者になる 読者になる

by shigemk2

当面は技術的なことしか書かない

Webアプリケーション HTTP通信のいろは

とりあえず復習を兼ねての参加。
Webアプリケーション HTTP通信のいろは : ATND

HTTP通信について

Webアプリケーションをきちんと理解する上で欠かせないのがHTTP

裏側で何が起きているかを知ることは、特に「若干面倒な設計」や「デバッグ」「トラブルヘッジ」で有益
AjaxなんかはHTTPの知識がないと混乱しやすい

TCP/IP

HTTP「通信」という言い方をする

IP 「パソコンに割り当てれている住所
パソコンのなかにあるNIC(Network Interface Card)に割り当てられた住所
「1枚」のNICに複数のIPとか複数のNICのなかに1つのIPが割り当てられることもある。

1NICあたり1IPがスタンダード

Only one のIPとそうではないのがある
グローバルIPアドレス(IPv4) 世界に1つしかないIP 枯渇してんのでIPv6に移行してれぅ

全世界のNIC(Network Interface Center)が一元管理している。(日本はJPNIC)

社内 家庭内でしか使えない代わりにNICにかかわらず好き勝手使えるIPもある
プライベートIPアドレス
e.g. 10.n.n.n 192.168.n.n 172.16.0.0 - 172.3.1.255.255

127.0.0.1 は自分自身

IPアドレス ネットワーク部 ホスト部に分かれる
ネットワーク部は村と一緒(同じ村のアドレスなら通信の行き来は自由だが、
違うネットワーク部のIPアドレスは全て拒絶する
(違うネットワーク部を繋ぐのがルータ)

ホスト部は個人名

サブネットマスク IPアドレスからネットワーク部を引っぱってくる

TCP Transmission Control Protocol ポート番号をあらわす

IPアドレスは1台のサーバを意味している
IPアドレス(サーバ)をマンションとすると、TCP(ポート番号)は部屋番号を指す

ポート番号にも種類がある

0-1023 特権ポート
1024 - 49151 登録済ポート
49152 - 65535 誰でも自由に使える

From IP TCP PCが適当にえらぶ
To IP http://example.com TCP 80番ポート

ブラウザでタブ ポート番号をずらしてからアクセスする

パケットの冒険

TCP/IPはfromとtoの組で、通信経路をがっつりと握って確保する

オレPC / 50000番 → www.example.com / 80番

通信はパケットという単位の信号がやりとりされる
通信の中身は大概文字列。

サーバ クライアント

リクエスト と レスポンスの応酬

サーバ は同時に10人くらいは捌けるけど同時に1000人は捌けないので、
あまり負荷をかけないこと。

供給を依頼するリクエスト と 要求に答えるレスポンス

名前: 値 というフォーマットでやりとりされる。

RFC Request for Comments でHTTPのルールは決まっている。
もとインターネットは軍事技術で、開発した技術を世に広めたい開発者が
「コメントを募集する」体で始めた。

リクエストもレスポンスも、ヘッダ 改行 ボディ で出来ている

HTTP1.1 RFC2616

1xx 受信され、処理中
2xx 受信完了
3xx 転送など、処理継続中
4xx リクエストエラー
5xx サーバエラー

基本的なリクエスト

GET /events/32184 HTTP/1.1
Host:atnd.org
User-Agent:Mozilla/5.0 うんたん ユーザーマシンの詳細
Cookie: _utima=xxxxxxxxxxxxxxxx
Referer: http://facebook.com/.....(どこのページからやってきたか 手入力によるアクセスだと分からない)

ブラウザの開発者ツールで分かるよ。

基本的なレスポンス

HTTP/1.1 200 OK
Date Date: Sat, 22 .....
Status: 200
Content-type: text/html; charset=utf-8 (文字コードUTF-8のHTMLを返したよ)
Content-Length: 30476
Set-Cookie: xxxxxx (次にこのサイトのアクセスするときはこのクッキー使ってくれよ)

細かいリクエスト
Accept: text/html,application/xhtml+xml,.... (受け入れ可能なタイプ)
Accept-Encoding: gzip, deflate (圧縮してよいか)
Connection:keep-alive(接続しっぱなしにしてよいか)

細かいレスポンス
Etag 前に見たものと変更がないかどうか突き合せる
Cache-Control キャッシュをどうしてほしいのかお願いする

If-None-Match: xxxx → HTTP/1.1 304 Not Modified Etag: xxxx

PerlPHPではどのように実装するのか

リクエストはブラウザが作る
レスポンスをプログラムでどうやって作るのか?

Perl

print "Content-type: text/html\n\n";

だけ

Content-Lengthをいれないといけないときもある。
Cookieはヘッダの一種なので、自分で文字列を組立て、
自分でprintする
場合によっていはレスポンス文を自分で出力しないといけない。

PHP

なにもしない
Cache-ControlはPerl同様なにもしないので、header関数で指定する…

<?php
echo 'test';
header('Cache-Control: no-cache');

なるエラーが起こる(既にレスポンスを書いているから)

testWarning: Cannot modify header information

これの対策(?)として、出力制御関数なるものが存在する

<?php
ob_start(); // 出力バッファ開始
echo 'test';
header('Cache-Control: no-cache');
echo 'test';
setcookie('name', 'value');
ob_end_flush(); // 出力バッファ終了 + バッファ送信

testtest

ヘッダーを出して改行しているからあとはボディしか出せないので、
出力バッファを利用する

Cookieとセッション

セッションの前提知識

  • authentication: 認証(最初のログイン)
  • authorization: 認可(2回目以降のログイン確認)

authorizationの無理ゲーっぷり

Keep-Aliveを使って繋ぎっぱなしにすることは可能ではあるが、
サーバが捌けるアクセスには限りがあるので、Keep-Aliveしつづけたら
破綻する。

本質的には不可能である。(リクエストとレスポンスがあったら通信は完了するから)

1回目のレスポンスのときに秘密の文字列(セッションID)を渡す
2回目以降のリクエストでセッションIDを送る

セッションIDはCookieを使ってやりとりすることが多い
たまにURIのうしろについてくることもある(古いガラケーにあるけど、Refererでサーチできるから蝶危険)

AさんのセッションIDは1
BさんのセッションIDは2
Cさんは、自分のクッキーを書き換えて本当は3なのに1と書いたら、次回のCさんのアクセスはAさんのものと見做される


推測可能なセッションIDはセッションの乗っ取りが発生するから蝶危険
セッションIDは長く変わらないのはよくない
PHPは認可の機能があるけど、そのままだと脆弱なので注意
(どの言語を使うにしてもリクエストやレスポンスの処理は強固にするべき)

携帯電話は、ごく一部のキャリアで、Cookieが使えません(しかもタチの悪いことにシェアは2割)
認可には、契約者固有IDが使われる(簡単ログインというのがこれ)

契約者固有IDは、User-Agentまたは専用のリクエストヘッダに含まれている
(この契約者固有IDは偽装可能なのですごくしんどい)
Googleは「簡単ログイン」へのサポートを今年終了した。

スマフォのUDID
ガラケーの契約者固有IDを流用したもの
iPhone
Android

しかし、やめたほうがいい。

レンダリング&動的画像生成 css

レンダリングの基本的な流れ

  1. HTMLをリクエスト
  2. レスポンスでHTMLが返ってくる
  3. HTMLを解析
  4. CSSをリクエスト
  5. CSSが返ってくる :@importがある場合は繰り返し
  6. JSをリクエスト
  7. IMGリクエスト

レンダリング紳士協定(同時接続コネクション数)
CSSやJS、画像がたくさんあっても、1ページを表示するときに
同時にサーバに依頼するのは2ファイルまでとする。

これ設定で変えれられるけど、やると嫌がられる。
ブログに「この設定でブラウザのレンダリングが高速に!!!」とか書くと、
サーバ管理系の方々から怒られます。

ロールオーバー
onMouseとかhoverのときに画像を入れ替えるやつ
画像の読み込みにもたつくのは同時接続コネクション数によるもの
づおしてもいやな場合は、「ロールオーバー 画像 1枚」でぐぐるとよい

鉄則その1
JSやCSSをあんまり細かい複数ファイルにすると、全体的にHTTPのリクエストとレスポンスが
たくさん発生し、全体として遅く、重くなるので、無理のない程度に少ないファイル数にまとめる。

FBのいいねはiframeなので、あれが縦に40コぐらいあるのも蝶危険(リクエストが200回を越えてた)
サーバーの管理はきちんとコントールすること

動的な画像生成 アバターとか
ゲームのユーザPageでアバター画像を出したい
アバター画像はユーザによって異なるから、「プログラムで動的に合成して」出力する

ユーザPageのプログラム
画像の合成
どこで画像のデータをprintするの…?

動的な画像を出すプログラムと静的な画像とHTMLを出力するプログラムは別にしないといけない。

実装は…
パラメタは不要
ユーザーPageは難しくない
画像を動的に作るプログラムは単体テストが可能
ブラウザで画像を生成するプログラムをたたく

余談
画像がうまく出力できないなら、Content-typeを間違えている可能性がある。
あるいは些細なミスが多い

PHPで?>を書くと面倒が起きやすい
画像を出力する際に先頭や終盤に余計な改行があったら
画像が表示されない。。。

問題ないケース

<?php
?>EOF

問題のあるケース

<?php
?>
EOF

debug printや警告表示がわるさしているかも。

ブラウザの機能で1回画像ファイルを保存してファイルを開くと、
先頭に変な読める文字が入っていたりするので手掛かりがつかめるかも。。。

JavaScript Ajaxあたり

JavaScriptはよくつかわれる

Ajax
XMLHttpRequestによる非同期通信を利用して、通信結果に応じてダイナミックHTMLで動的にページの一部を書き換える仕組み

HTTPはリクエストして、レスポンスが返ってきて、Pageをレンダリングしなおす
例えば左のメニューをちょこっと修正したいだけなのに、アクセスのたびに全ページを
レンダリングしなおすのは重いしうざい。

JavaScriptはイベントドリブンであり、つまり「何かしたら、何かが起きる」ということ。

イベントの例
Clickした
マウスが自分の上を通った
マウスがいなくなった

など…

動作の例
Popupが出る
サーバに情報を送る
計算をする


Twitterのツイート部分
facebookの未読表示

JavaScriptはDOM構造を変える

DOM(Document Object Model) DOMはツリー構造

JSはDOM構造を弄って表示を変更したりできる

何かをきっかけに、XMLHttpRequestによる非同期通信を発生させる
レスポンスを受けとる
レスポンスの結果をごにょごにょして、DOM構造をぶちこむ

ユーザからはPage「遷移」はしていないけど、Pageの一部が書きかわったように「見える」

もう紙芝居はしない。

Ajaxでリクエストが発生しレスポンスがきたら、
functionを実行させるように設定しておくことも可能

XMLHttpRequest.onreadystatechange = function

適宜な処理の必要性
戻り値がHTMLの場合は、そのままDOM要素をいれる場合がおおい
戻り値がXMLやJSONの場合は、適宜HTMLを組立てからDOM要素を要れる場合がおおい(パースしてHTMLっぽい文字列を作る)
戻り値がエラーの場合 適宜エラー処理をする

どちらにしてもXSSには注意(フォームにJSのコードを入れて、実行されたらどうすんの?)

その他
定期的にAjaxでコンテンツを入れ替えたり、新着情報を確認したりするときは、アクセス頻度に注意
頻度上げすぎるとアクセスなのかアタックなのかわかんなくなるからやめて

PVのカウントに気をつけて (ページのコンテンツのほとんどにAjaxが使われると、何を以ってアクセスと言うのか
分からなくなるから、マーケターが困る *jpegとかまでPVカウントするところもあるけど)

出来ればJS側でもテンプレートエンジンを使う
デザインのテンプレを用意しており、これを使うとXSS対策にもなりうる デザインも修正されづらい

おまけ
スマフォアプリのサーバと通信には、往々にしてHTTP通信が使われる
特にスマフォアプリは一部Ajaxを使うことがあるので、乱用しない程度に独自ヘッダを使うと便利。

まとめ

HTTP通信は裏方だが、理解していると今何が起きているかが分かりやすくなる
理解は正常時ではなく異常時に役立つ
WebだとSMTPがとても役に立つ

問題解決のために、知識を沢山有しておくとよいね。


Cacheまわりの設定をApacheで行うことも出来る