SSIでsocketを使うプログラムを呼び出す

[上に] [前に] [次に]
miyu 1999/07/23(金) 20:33:22
WWWサーバA: CGIは使用不可,SSIは動作可能
WWWサーバB: CGI, SSIどちらも動作可能
という環境があります.AのページからSSIでBにあるCGIプログラム
を呼び出して表示させるプログラムを作っています.
Aのページから<A HREF="http://B/~miyu/foo.cgi">…というように
呼び出せばいいのですが,Bのサーバ名を出したくありません.

以下のようなスクリプトを書いてサーバAに置いて,
<!---exec cmd="〜"--->
で呼び出してみたのですが,スクリプトとして認識している
ようなのですが,なにも表示させません.

Perlは,とほほさんのページや他のスクリプトを参考に書き換える程度
にしか分かりません.どう直せばいいのかご助言をお願いします.

-----begin------
$post_parameter = "hogehoge=hagahaga&..."
$addr = (gethostbyname("サーバBのホスト名"))[4];
$method = "POST";
$url    = "~miyu/foo.cgi";
$ipname = pack("S n a4 x8", 2, 80, $addr);

#-----------------------------------------変数のセット--------------
&make_socket;
print @lines;

#-----------------------------------------データ送信--------------
sub make_socket{
socket(SOCKET, 2, 1, 0) || die "Can't socket\n";
connect(SOCKET, $ipname) || die "Can't connect\n";
select(SOCKET); $| = 1; select(STDOUT);

if( $method eq "GET" ){
  print SOCKET "$method /$url?$post_parameter HTTP/1.0\r\n";
}else{
  print SOCKET "$method /$url HTTP/1.0\r\n";
}
if( $method eq "POST" ){
  $len = length($post_parameter);
  print SOCKET "Content-length: $len\r\n";
  print SOCKET "\r\n";
  print SOCKET "$post_parameter\r\n";
}
print SOCKET "User-Agent: $ownscript\r\n";
print SOCKET "\r\n";

#----------------------------------------データ受信---------------
$i=0;

if( $method eq "GET" || $method eq "POST" ){
  print "$i\n";
  while(<SOCKET>){  m/^\r\n$/ && last;  $i++;}
  while(<SOCKET>){
   if( $_ =~ m/^<HR>.*/ ){ $lines[$i++] = $_; }#foo.cgiの出力結果を一部抜粋
   $i++;
  }
}elsif( $method eq "HEAD" ){
  while(<SOCKET>){   print $_;  }
}
}

miyu 1999/07/23(金) 20:36:24
この,「変数のセット」というのは間違いなので,無視して下さい
>#-----------------------------------------変数のセット--------------
>&make_socket;
>print @lines;

B-Cus 1999/07/23(金) 22:32:45
ちゃんと読んでないので原因はわからんけど、
> socket(SOCKET, 2, 1, 0) || die "Can't socket\n";
などとしないで、
 if ( ! socket(SOCKET, 2, 1, 0) ){ print "Error in socket"; exit; }
として、どこがおかしいかデバックしてみては。
 close(STDERR);
 open(STDERR,">&-");
としておけばdieでもいいんだっけ?(">-"かもしれない)

> <!---exec cmd="〜"--->
正しくは <!--#exec cmd="〜" -->

miyu 1999/07/23(金) 23:07:10
早速のアドバイスありがとうございます.
>if ( ! socket(SOCKET, 2, 1, 0) ){ print "Error in socket"; exit; }
は何も表示されませんでした.

>if( $method eq "GET" || $method eq "POST" ){
>print "$i\n";

のところでは,`0'と$iの値が表示されます.あと,
サーバBのアクセスログには,記録されていませんでした.

B-Cusさんも書かれているように,エラーメッセージを
うめこんでどこでエラーが出ているのか表示させたいのですが,
書き方が良く分からないので,それもあわせてご教示ください.

> close(STDERR);
> open(STDERR,">&-");

これはどこに埋め込めばいいのでしょうか?

B-Cus 1999/07/24(土) 14:05:29
原因を調べるのにかなり苦労してしまった。これで全部外れてたら悲しいなぁ。

-------------------------------------------------------------------------
このスクリプトって、http://www.tohoho-web.com/wwwperl2.htm#socket
を参考にしたんですよね? 僕はいつも Socketモジュールを使ってるんで
(要perl5)もしかしたら間違ってるかもしれませんが、と前置きした上で…

------------------------------------------------------------------------
> socket(S, 2, 1, 0);
問題はここじゃないですかねぇ。以下は基礎知識。

  socketの引数は、順に、ソケット名、ドメインタイプ(って言うのかな)、
  ソケットタイプ、プロトコルタイプを意味します。

  ドメインタイプで
   ・AF_UNIX(UNIXドメインソケット)かAF_INET(インターネットドメインソケット)
  を指定します。ま、この場合インターネットドメインソケットにするべきです。

  ソケットタイプで
   ・SOCK_STREAM(TCP/IP)かSOCK_DGRAM(UDP/IP)か
  を選びます。

  プロトコルタイプは、ほんとはIPとかTCPとかICMPとか指定するんだけど、
  0にしとけば勝手に設定されます。

で、socket(S,2,1,0)ってのはAF_INET、SOCK_DGRAMを意味するわけなんです。
要はインターネットで、UDP/IPを使うって意味。

がしかし。WWWサーバってのはUDP/IPじゃなくてTCP/IPで待ってる方が多い。
というか、ほとんどTCP/IPで待ってます。
# ここらへんよくわかりません。昔読んだ本には「HTTPはUDPである」と
# 確かに書いてありました。しかしRFCには
#  HTTP communication usually takes place over TCP/IP connections.
# とありますし、TCPで待ってる方がほとんどではないかと思います。

ですから、うちの環境では socket(S,2,2,0) にしたら動きました。
ただし、たまにUDPでも待ってるWWWサーバも存在します。たまたま
そういうところにあたると、UDPでも動作するでしょう。
# ここらへんの状況について、情報求む。

------------------------------------------------------------------------
あと、http://www.tohoho-web.com/wwwperl2.htm#socket
サンプルということでこういう書き方になってますが、絶対にお勧めしません。
ソースに直接マジックナンバ(意味のわからない数字)を埋め込むのは
絶対やってはいけません。

perl5を使ってるなら Socketモジュールを使って
 http://www.cs.gunma-u.ac.jp/~j5306050/net/http-2.html
という書き方をしましょう。ポートは80とわかっていても getservbynameを使う。
2じゃなくてPF_INETと書く。
 socket(SERVER,2,2,0)
 socket(SERVER,PF_INET,SOCK_STREAM,$proto)
どちらが理解しやすいかはわかりますよね?

もちろんさらに便利なLWPモジュールを使っても可。
# SocketモジュールはPerl5に標準で付いてきます。
# LWPモジュールは自分でインストールする必要があります。

なので、できればもう少しわかりやすい記述を希望します>とほほさん
# まぁわかりやすくしようとすると、結局全部説明するはめになるんですが…

------------------------------------------------------------------------
あと、別の可能性もあります。SSIを動かしているサーバから外部へは、
proxyを経由しないと行けない場合。そのときは
 GET /$url HTTP/1.0\r\n
ではなく
 GET http://接続したいWWWサーバのホスト名/$url HTTP/1.0\r\n
としませう。当然connectするのはproxyサーバね。

------------------------------------------------------------------------
あとさ、POSTの書式 違ってません? $post_parameterは最後に
付けるもんだと思っていたんですが(確認してないので違うかも)。

------------------------------------------------------------------------
あと、デバッグ中は問題を切り分けるために、プログラムを最小単位まで
絞り込みましょう。

POSTやらHEADなんて放っておけばいいんです。そんなのGET(HEADでもいいけど)が
動いてから考えればいいんだから。僕は以下のように不要な部分を削って
以下のような状態からデバッグを始めました。

$addr = (gethostbyname("サーバBのホスト名"))[4];
$url = "/index.html";
$ipname = pack("S n a4 x8", 2, 80, $addr);
socket(SOCKET, 2, 1, 0);
connect(SOCKET, $ipname);
select(SOCKET); $| = 1; select(STDOUT);
print SOCKET "GET /index.html HTTP/1.0\r\n\r\n";
while(<SOCKET>){print;}

------------------------------------------------------------------------
> これはどこに埋め込めばいいのでしょうか?
スクリプトの先頭です。dieの文字列がSTDOUTに出力されます。

miyu 1999/07/25(日) 23:19:54
[[解決]]
>> socket(S, 2, 1, 0);
>問題はここじゃないですかねぇ。以下は基礎知識。

まさしく,問題はここにありました.
socket(S, 2, 2, 0);
としたところ,動作しました.

ご指摘のように,マジックナンバーを埋め込むプログラムは
良くないのは承知しているのですが,とほほさんの
socketの部分をそのまま使っていて,この数字の意味が
よくわからず,どう変えればいいのか全然分からなかったので
そのままにしていました.今回の,B-Cusさんの解説で
よく分かりました.どうもありがとうございました.
また,B-Cusさんのページも参考にしてみます.

これで,なんとかプログラムができそうなので,
解決マークをチェックしておきます.

B-Cus 1999/07/26(月) 02:59:05
すまんす。嘘入ってました。

> WWWサーバってのはUDP/IPじゃなくてTCP/IPで待ってる方が多い。
> というか、ほとんどTCP/IPで待ってます。
これはたぶん嘘。HTTPはUDPではなくTCP。

> 昔読んだ本には「HTTPはUDPである」と確かに書いてありました。
これは勘違いの可能性が高いです。


Cでは socket の引数として、SOCK_STREAM とか SOCK_DGRAM とかを
使います。これは (/usr/include/)sys/socket.h に定義されています。
家のFreeBSDでは
  #define SOCK_STREAM 1 /* stream socket */
  #define SOCK_DGRAM 2 /* datagram socket */
となっていました。なので、socket(2,1,0)でも動きました。
# そのときは勘違いしてて、今アクセスしたサーバはUDPなんだろう
# と思っていました

しかし、Solarisだと
  #define SOCK_STREAM 2 /* stream socket */
  #define SOCK_DGRAM 1 /* datagram socket */
となっており、TCPとUDPの値が全く逆になっています。ですので、
http://www.tohoho-web.com/wwwperl2.htm#socket の socket(2,1,0) は、
FreeBSDでは TCP/IP を意味し、Solarisでは UDP/IPを意味しますので、
サンプルとしてはまずいかと>とほほさん

perl5だと前述のように
 use Socket;
 socket(SERVER,PF_INET,SOCK_STREAM,$proto)
と書けますが、perl4だと、どうしようもない…のかな?
古いラクダ本では、どういうコーディングしてるんでしょうか。

miyu 1999/07/26(月) 16:13:25
>FreeBSDでは TCP/IP を意味し、Solarisでは UDP/IPを意味します
CではなくPerlなのですが,思い当たる節があります.
実は,最初の記事のサーバAはSolaris,
サーバBはFreeBSDなのです.
サーバBに,上で挙げたプログラムのままで単独で動かすとちゃんと接続できました.CとPerlでは単純に比較できませんが...

[上に] [前に] [次に]