| 前へ << HTTP クライアントを作ってみよう(2) | HTTP クライアントを作ってみよう(4) >> 次へ |
前節で説明したサンプルは、一応は HTTP クライアントとして最低限の動作はしますが、 もう少し機能を追加してみましょう。の4点を改善してみます。以下に詳しく説明します。
- ダウンロードする URL を指定できるようにする
- User-Agent 情報を送る
- Proxy に対応する
- HEAD に対応する
さきほどのサンプルでは http://www.cs.gunma-u.ac.jp/ にしかアクセスできませんでした。 コマンドラインで任意のURLを指定できるようにしてみます。
User-Agent については User Agent統計 を参照してください。要は HTTP クライアントの自己紹介です。 User-Agent は「クライアント名/バージョン (OSなどの情報)」 という形式にすることが推奨されています。クライアント名は…まぁ何でもいいのですが、とりあえず 「httptalker」としましょう。 勉強用のサンプルなのでバージョンは 0.10 とします。 結局 User-Agent として「httptalker/0.10 (HTTP client sample)」を送ることにします。
サーバへの User-Agent の渡し方ですが、
% telnet www.cs.gunma-u.ac.jp 80 Trying 133.8.2.7... Connected to www.cs.gunma-u.ac.jp. Escape character is '^]'. GET / HTTP/1.0(リターン) User-Agent: httptalker/0.10 (HTTP client sample)(リターン) (リターン)と、メソッドの後につづけて User-Agent を送信します。
Proxy サーバを経由して WWW サーバにアクセスできるようにします。 多くの場合、Proxy サーバはポート 8080 で動いているようですが、 8080 以外のポートを使う Proxy サーバも存在します (普通 Proxy サーバの設定で変えることができます)。telnet を使って、proxy.example.com:8080 を経由して http://www.cs.gunma-u.ac.jp/ にアクセスしてみましょう。
% telnet proxy.example.com 8080 Trying 10.11.12.13... Connected to proxy.example.com Escape character is '^]'. GET http://www.cs.gunma-u.ac.jp/ HTTP/1.0(リターン) (リターン)これまでと違うところは、目的の WWW サーバ (この場合はwww.cs.gunma-u.ac.jp) に 直接繋げるのではなく、proxy サーバに接続している点です。ポート番号も 80 ではなく 8080 にします。そして、送信するリクエストの内容がGET / HTTP/1.0(リターン)ではなくGET http://www.cs.gunma-u.ac.jp/ HTTP/1.0(リターン)と、URL をそのまま渡しています。proxy.example.com で動いている proxy サーバは、あなたの代わりに www.cs.gunma-u.ac.jp のポート 80 に接続して、GET / HTTP/1.0(リターン)というリクエストを送るわけです。そして返ってきたデータを あなたのところに送ってくるのです。proxy サーバは「代理サーバ」と 訳される理由がわかりますね。 あなたの代わりに目的のホストに接続してくれるのが proxy サーバなのです。
WWW サーバは、クライアントから GET コマンドが送られてくると、 ヘッダ・空行・ボディを返します。一方、HEAD コマンドが送られて くるとヘッダしか返しません。ヘッダ部分の一番先頭の行はステータスと呼ばれる情報となっており、 そこを見れば「Not Found」「Permission Denied」「Internal Server Error」などの状態を 判断できます。よく知られているのは
HTTP/1.1 200 OK HTTP/1.1 301 Moved Permanently HTTP/1.1 404 Not Found HTTP/1.1 500 Internal Server Errorといったものですが、みなさんもだいたいの意味はわかるでしょう。GET に比べて HEAD はボディを転送しない分だけ、送受信する データ量は少なくなり、その結果処理にかかる時間も短くてすみます。 そのため、リンク先がつながっているかどうかを調べるための リンクチェッカなどが HEAD メソッドをよく使います。
http://www.cs.gunma-u.ac.jp/ のヘッダ情報だけを受け取るには以下のようにします。
% telnet www.cs.gunma-u.ac.jp 80 Trying 133.8.2.7... Connected to www.cs.gunma-u.ac.jp. Escape character is '^]'. HEAD / HTTP/1.0(リターン) (リターン)
最後に、User-Agent を送信しつつ、 http://www.cs.gunma-u.ac.jp/ のヘッダ情報だけを proxy サーバ (proxy.example.com:8080) を 通じて受け取ってみましょう。要は、これまでやったことを組み合わせるだけでいいのです。% telnet proxy.example.com 8080 Trying 10.11.12.13... Connected to proxy.example.com Escape character is '^]'. HEAD http://www.cs.gunma-u.ac.jp/ HTTP/1.0(リターン) User-Agent: httptalker/0.10(リターン) (リターン)
改良版 HTTP クライアントの書式はhttptalker -METHOD URL [Proxy]です。METHOD には GET か HEAD を指定します。URL は http://www.hoge.com/fuga.html などです。Proxy には proxy.example.com:8080 などと指定しますが、省略可能です。 例えば% ./httptalker -GET http://www.hoge.com/fuga.html % ./httptalker -GET http://www.hoge.com/fuga.html proxy.example.com:8080 % ./httptalker -HEAD http://www.hoge.com/fuga.htmlのように指定します。ソースは以下のようになります。1: #!/usr/local/bin/perl -w 2: 3: # $Id: http-client-2.pl,v 1.2 2002/02/05 17:53:09 68user Exp $ 4: 5: use Socket; # Socketモジュールを使う 6: 7: #----------------引数解析-------------------- 8: 9: # メソッドを解析。-HEADか-GET以外ならエラー 10: if ( $ARGV[0] eq "-HEAD" || $ARGV[0] eq "-head" ){ 11: $method = "HEAD"; 12: } elsif ( $ARGV[0] eq "-GET" || $ARGV[0] eq "-get" ){ 13: $method = "GET"; 14: } else { 15: print "methodはGETかHEADを指定してください。\n"; 16: exit; 17: } 18: 19: # URLを解析。http://host/pathという形式でなければエラー 20: if ( $ARGV[1] =~ m|^http://([-_\.a-zA-Z0-9]+)/?(.*)$| ){ 21: $host = $1; 22: $path = $2; 23: } else { 24: print "URLは http://host/path という形式で指定してください。\n"; 25: exit; 26: } 27: 28: # 引数が3つあるならProxyを解析 29: if ( $#ARGV == 2 ){ 30: if ( $ARGV[2] =~ m|^([-_\.a-zA-Z0-9]+):(\d+)$| ){ 31: $proxy = $1; 32: $port = $2; 33: $connect_host = $proxy; 34: } else { 35: print "Proxy は host:port という形式で指定してください。\n"; 36: exit; 37: } 38: $connect_host = $proxy; 39: 40: # 引数が2つしかないなら 41: } else { 42: $connect_host = $host; 43: $port = getservbyname('http', 'tcp'); 44: } 45: 46: 47: #----------------接続処理------------------- 48: 49: # ホスト名を、IPアドレスの構造体に変換 50: $iaddr = inet_aton($connect_host) 51: || die "$connect_hostは存在しないホストです。\n"; 52: 53: # portとIPアドレスとまとめて構造体に変換 54: $sock_addr = pack_sockaddr_in($port, $iaddr); 55: 56: # ソケット生成 57: socket(SOCKET, PF_INET, SOCK_STREAM, 0) 58: || die "ソケットを生成できません。\n"; 59: 60: # 指定のホストの指定のportに接続 61: connect(SOCKET, $sock_addr) 62: || die "$connect_host の ポート$portに接続できません。\n"; 63: 64: # ファイルハンドルSOCKETをバッファリングしない 65: select(SOCKET); $|=1; select(STDOUT); 66: 67: 68: #------------HTTPリクエスト送信----------------- 69: 70: # Proxyサーバに接続するなら 71: if ( defined $proxy ){ 72: print SOCKET "$method http://$host/$path HTTP/1.0\r\n"; 73: # 直接WWWサーバに接続するなら 74: } else { 75: print SOCKET "$method /$path HTTP/1.0\r\n"; 76: } 77: 78: # User-Agentを送信 79: print SOCKET "User-Agent: httptalker/0.10 (HTTP client sample)\r\n"; 80: print SOCKET "\r\n"; 81: 82: 83: #------------サーバからのデータを受信 ----------------- 84: 85: if ( $method eq "GET" ){ 86: # GETメソッドならヘッダ部分は表示せず 87: while (<SOCKET>){ 88: m/^\r\n$/ && last; 89: } 90: # ボディ部分だけを表示する 91: while (<SOCKET>){ 92: print $_; 93: } 94: } else { 95: # HEADメソッドなら全文表示 96: while (<SOCKET>){ 97: print $_; 98: } 99: }主な変更点は、引数解析、GET と HEAD の場合分け、Proxy を経由するかどうかの場合分けです。解説は…書かなくても大丈夫ですよね? 前バージョンと見比べてみれば理解できると思います。
さて、改良はしたものの、まだまだHTTP クライアントとしては不完全です。 次節で問題点を上げますので、各自で改良してみてください。
| 前へ << HTTP クライアントを作ってみよう(2) | HTTP クライアントを作ってみよう(4) >> 次へ |