Memo/PHP
phpでSSL通信 †
目的 †
https://〜/へリクエストを投げる事。
サーバー側プログラムは通常通り標準出力すれば、apache等のwebサーバーが勝手にSSLで処理してくれます。
モジュール | バージョン | cURL | 7.13.0(2005-02-18 (金) 16:18:09) | PEAR Net_Curl | 0.2(stable) / 1.0.1(beta)2005-02-18 (金) 16:39:06 |
バグ †
- Windowsだと、php.iniに「extension=php_openssl.dll」を指定しても動作しない。
staticリンクだと動作する模様。
cURLを使用する場合 †
PEAR Net_Curlを使用する場合 †
0.2と1.0.1betaがあるが、最新の1.0.1betaで実験してみる。
内部でcURLを呼んでいるようなので、cURLが必要。
インストール †
pear install Net_Curl-beta
ソケット通信 †
fsockopen()で行う場合。
PHPのBugがあるので注意。
- メリット
- cURLがなくともOpenSSLが入っていれば使える。(大抵は標準で入ってる)
- 関数が一つのなのでお手軽
- 細かい処理に対応できる
- デメリット
- メンテナンスは自前:p
<?php
/**
* @file
* @brief HTTP関係クラス
*/
/**
* HTTPリクエストの送信(GET, POST, SSL, NameVirtualHost, Basic認証対応)
*
* @param string $url URL ex: http://example.com:80/path/to, https://example.com:443/path/to, tls://example.com:443/path/to
* @param array $options
* 'content' => array('key'=>'value'), 送信データ
* 'method' => 'GET' or 'POST' デフォルト:'GET'
* 'header' => array(
'User-Agent:' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)'
), httpヘッダ
* 'host' => 'IPアドレス', (省略可。ネームベースのバーチャルホストで特定のサーバにアクセスしたい場合は、IPアドレスを指定)
* 'time_limit' => 10, (タイムアウト秒数。省略可。デフォルト10秒)
* 'errno' => エラーがある場合はエラー番号,
* 'errstr' => エラーがある場合はエラー文字列,
* @return array list($head, $body)
*
* @note
* - file_get_contents()では Location:ヘッダ + bodyがあった場合にリダイレクトされ、bodyを取得できない。この関数はリダイレクトしないため、bodyを取得できる。
* - 例
* - GET:list($head, $body) = httpRequest("http://example.com/path/to?key1=val1", $options);
* - GET:
@code
$options = array('method'=>'get', 'content'=>array('key1' => 'val1') );
list($head, $body) = httpRequest("http://example.com/path/to", $options);
@endcode
* - GET(SSL):
@code
$options = array('method'=>'get', 'content'=>array('key1' => 'val1') );
list($head, $body) = httpRequest("https://example.com/path/to", $options);
@endcode
* - POST:
@code
$options = array('method'=>'post', 'content'=>array('key1' => 'val1') );
list($head, $body) = httpRequest("http://example.com/path/to", $options);
@endcode
* - エラーの判定
@code
list($head, $body) = httpRequest("http://example.com/path/to", $options);
if($options['errno'] != 0){
// エラー処理
echo $options['errstr'] . "\n";
}
@endcode
* - NameVirtualHostの特定ホストにリクエストを送る
@code
$options = array('host'=>'xxx.xxx.xxx.xxx');
list($head, $body) = httpRequest("http://example.com/path/to", $options);
@endcode
* - GET: 8080ポートを指定
@code
$options = array('method'=>'get', 'content'=>array('key1' => 'val1') );
list($head, $body) = httpRequest("http://example.com:8080/path/to", $options);
@endcode
* - basic認証
@code
list($head, $body) = httpRequest("http://username:password@example.com/path/to", $options);
@endcode
* - 任意のhttpヘッダを送信する
@code
$options = array( 'header'=>array('User-Agent:' => "PHP/".phpversion() ) );
list($head, $body) = httpRequest("http://username:password@example.com/path/to", $options);
@endcode
*/
function httpRequest($url, &$options)
{
$_options = array(
'content' => '', // postデータ
'method' => 'GET', // 'post' or 'get'
'header' => array(
'Accept-Language:' => 'ja',
'User-Agent:' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)', // IE7 or "User-Agent: PHP/".phpversion()."\r\n";
),
'host' => '',
'time_limit' => 10,
);
$options['errno'] = -1;
$options['errstr'] = '';
$_options = array_merge($_options, $options);
$url = trim($url);
$purl = parse_url($url);
$purl['scheme'] = strtolower($purl['scheme']);
switch($purl['scheme']){
case 'http':
$purl['scheme'] = '';
$purl['port'] = ($purl['port'] != '') ? $purl['port'] : 80;
break;
case 'https':
$purl['scheme'] = 'ssl://';
$purl['port'] = ($purl['port'] != '') ? $purl['port'] : 443;
break;
case 'tls':
$purl['scheme'] = 'tls://';
$purl['port'] = ($purl['port'] != '') ? $purl['port'] : 443;
break;
default:
break;
}
// Build the request string
$request = '';
if($_options['content'] != ''){
if(is_array($_options['content'])){
$request = http_build_query($_options['content']);
}else{
$request = $_options['content'];
}
}
if (isset($purl["query"])) {
$request .= ($request == '') ? $purl["query"] : '&'.$purl["query"];
}
if($_options['host'] != ''){ // ホストが指定されている場合
$purl["host"] = $_options['host'];
}
$request_length = strlen($request);
// Build the header
$_options['method'] = strtoupper($_options['method']);
$header = '';
$x_header = '';
if( is_array($_options['header']) ){
foreach($_options['header'] as $key => $val){
if(!preg_match("/\:$/",$key) ) $key .= ':';
$x_header .= sprintf("%s %s\r\n", $key, $val);
}
}else{
$x_header = $_options['header'];
if(!preg_match("/\r\n$/",$x_header) ) $x_header .= "\r\n";
}
switch($_options['method']){
case 'POST':
$header .= "POST {$purl["path"]} HTTP/1.0\r\n";
$header .= "Host: {$purl["host"]}\r\n";
$header .= $x_header;
$header .= "Content-type: application/x-www-form-urlencoded\r\n";
$header .= "Content-length: {$request_length}\r\n";
break;
case 'GET':
$header .= "GET {$purl["path"]}?$request HTTP/1.0\r\n";
$header .= "Host: {$purl["host"]}\r\n";
$header .= $x_header;
$request = '';
break;
default:
$header .= "$method {$purl["path"]} HTTP/1.0\r\n";
$header .= "Host: {$purl["host"]}\r\n";
$header .= $x_header;
break;
}
// Basic認証用ヘッダ
if (isset($purl['user']) || isset($purl['pass'])) {
$header .= "Authorization: Basic ".base64_encode($purl['user'].":".$purl['pass'])."\r\n";
}
$header .= "\r\n";
// Open the connection
$fp = @fsockopen($purl['scheme'] . $purl["host"], $purl["port"], $options['errno'], $options['errstr'], $_options['time_limit']);
if (!$fp) {
if ( $options['errno'] == 0 ){
$options['errno'] = 500;
$options['errstr'] = 'fsockopen failed';
}
return array();
}
if(is_int($_options['time_limit'])) socket_set_timeout($fp, $_options['time_limit']);
// Send everything
fputs($fp, $header . $request);
// Get the response
$response = '';
while (!feof($fp)) {
// PHP Bug #23220 「Warning: fgets(): SSL: fatal protocol error」の抑制
// file_get_contents()でも起こるようだ。
$response .= @fgets($fp, 4096);
}
fclose($fp);
$fp = NULL;
$DATA = split("\r\n\r\n", $response, 2);
if(preg_match('#^HTTP/[\d\.]+\s+(\d+)[^\r\n]+#i', $DATA[0], $matches)){
$status_code = intval($matches[1]);
if($status_code >= 200 && $status_code <= 299){
// 2xx Success
}else if($status_code >= 300 && $status_code <= 399){
// 3xx Redirection
}else{
$options['errno'] = $status_code;
$options['errstr'] = $matches[0];
}
}
return $DATA;
}
|
|