Memo/PHP/phpでSSL通信

https://dexlab.net:443/pukiwiki/index.php?Memo/PHP/php%A4%C7SSL%C4%CC%BF%AE
 

Memo/PHP

phpでSSL通信

目的

https://〜/へリクエストを投げる事。
サーバー側プログラムは通常通り標準出力すれば、apache等のwebサーバーが勝手にSSLで処理してくれます。

モジュールバージョン
cURL7.13.0(2005-02-18 (金) 16:18:09)
PEAR Net_Curl0.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;
      }

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2018-09-15 (土) 07:31:38 (3d)