<?php
class http{
	//
	// Post provided content to an http server and optionally
	// convert chunk encoded results.  Returns false on errors,
	// result of post on success.
	//
	static function httpPost( $ip = null, $port = 80, $uri = null, $content = null, $prefix = '', $addPortToHost = true, $logfile = null, $aHeaders = Array() ){
		if( empty( $content ) ){
			if( !empty( $logfile ) ) file_put_contents( $logfile, 'empty content', FILE_APPEND );
			return false;
		}
		if( !isset( $aHeaders['Content-Type'] ) )
			$aHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
		if( !isset( $aHeaders['Content-Length'] ) )
			$aHeaders['Content-Length'] = strlen( $content );
		return self::httpRequest( 'POST', $ip, $port, $uri, $content, $prefix, $addPortToHost, $logfile, $aHeaders );
	}
	static function httpGet( $ip = null, $port = 80, $uri = null, $prefix = '', $addPortToHost = true, $logfile = null, $aHeaders = Array() ){
		return self::httpRequest( 'GET', $ip, $port, $uri, null, $prefix, $addPortToHost, $logfile, $aHeaders );
	}
	static function httpRequest( $method = 'GET', $ip = null, $port = 80, $uri = null, $content = null, $prefix = '', $addPortToHost = true, $logfile = null, $aHeaders = Array() ){
		if( empty( $ip ) ){
			if( !empty( $logfile ) ) file_put_contents( $logfile, 'empty ip', FILE_APPEND );
			return false;
		}
		if( !is_numeric( $port ) ){
			if( !empty( $logfile ) ) file_put_contents( $logfile, 'not numeric port', FILE_APPEND );
			return false;
		}
		if( empty( $uri ) ){
			if( !empty( $logfile ) ) file_put_contents( $logfile, 'empty uri', FILE_APPEND );
			return false;
		}
		// generate headers in array.
		$t = array( );
		$t[] = $method . ' ' . $uri . ' HTTP/1.1';
		if( !isset( $aHeaders['Host'] ) )
			$t[] = 'Host: ' . $ip . ( $addPortToHost ? ':' . $port : null );
		if( !isset( $aHeaders['Connection'] ) )
			$t[] = 'Connection: close';
		foreach( $aHeaders as $sHeaderName => $sHeader ){
			$t[] = $sHeaderName . ': ' . $sHeader;
		}
		$t = implode( "\r\n", $t ) . "\r\n\r\n" . $content;
		//
		// Open socket, provide error report vars and timeout of 10
		// seconds.
		//
		$fp = @fsockopen( $prefix . $ip, $port, $errno, $errstr, 10 );
		// If we don't have a stream resource, abort.
		if( !( get_resource_type( $fp ) == 'stream' ) ){
			if( !empty( $logfile ) ) file_put_contents( $logfile, 'no stream', FILE_APPEND );
			return false;
		}
		if( !empty( $logfile ) ) file_put_contents( $logfile, 'sending: ' . $t, FILE_APPEND );
		//
		// Send headers and content.
		//
		if( !fwrite( $fp, $t ) ){
			if( !empty( $logfile ) ) file_put_contents( $logfile, 'error sending headers and content', FILE_APPEND );
			fclose( $fp );
			return false;
		}
		//
		// Read all of response into $rsp and close the socket.
		//
		$rsp = '';
		while( !feof( $fp ) ){
			$rsp.= fgets( $fp, 8192 );
		}
		fclose( $fp );
		//
		// Call parseHttpResponse() to return the results.
		//
		if( !empty( $logfile ) ) file_put_contents( $logfile, 'sent and returning response: ' . $rsp, FILE_APPEND );
		return self::parseHttpResponse( $rsp );
	}
	//
	// Accepts provided http content, checks for a valid http response,
	// unchunks if needed, returns http content without headers on
	// success, false on any errors.
	//
	static function parseHttpResponse( $content = null ){
		if( empty( $content ) ){
			return false;
		}
		// split into array, headers and content.
		$hunks = explode( "\r\n\r\n", trim( $content ) );
		if( !is_array( $hunks ) or count( $hunks ) < 2 ){
			return false;
		}
		$header = $hunks[count( $hunks ) - 2];
		$body = $hunks[count( $hunks ) - 1];
		$headers = explode( "\n", $header );
		unset( $hunks );
		unset( $header );
		if( !self::validateHttpResponse( $headers ) ){
			return false;
		}
		if( in_array( 'Transfer-Coding: chunked', $headers ) || in_array( 'Transfer-Encoding: chunked', $headers ) ){
			return trim( self::unchunkHttpResponse( $body ) );
		} else{
			return trim( $body );
		}
	}
	//
	// Validate http responses by checking header.  Expects array of
	// headers as argument.  Returns boolean.
	//
	static function validateHttpResponse( $headers = null ){
		if( !is_array( $headers ) or count( $headers ) < 1 ){
			return false;
		}
		switch( trim( strtolower( $headers[0] ) ) ){
			case 'http/1.0 100 ok':
			case 'http/1.0 200 ok':
			case 'http/1.1 100 ok':
			case 'http/1.1 200 ok':
				return true;
			break;
		}
		return false;
	}
	//
	// Unchunk http content.  Returns unchunked content on success,
	// false on any errors...
	//
	static function unchunkHttpResponse( $str = null ){
		if( !is_string( $str ) or strlen( $str ) < 1 ){
			return false;
		}
		$eol = "\r\n";
		$add = strlen( $eol );
		$tmp = $str;
		$str = '';
		do{
			$tmp = ltrim( $tmp );
			$pos = strpos( $tmp, $eol );
			if( $pos === false ){
				return false;
			}
			$len = hexdec( substr( $tmp, 0, $pos ) );
			if( !is_numeric( $len ) or $len < 0 ){
				return false;
			}
			$str.= substr( $tmp, ( $pos + $add ), $len );
			$tmp = substr( $tmp, ( $len + $pos + $add ) );
			$check = trim( $tmp );
		} while( !empty( $check ) );
		unset( $tmp );
		return $str;
	}
	//
	// Php returns content with additional length makers
	// This function gets rid of them :)
	//
	static function parsePhpResponse( $str = null ){
		if( !is_string( $str ) || empty( $str ) ){
			return false;
		}
		$eol = "\r\n";
		$add = strlen( $eol );
		$tmp = $str;
		$str = '';
		while( !empty( $tmp ) ){
			$lenhex = substr( $tmp, 0, strpos( $tmp, $eol ) );
			$len = hexdec( $lenhex );
			$str.= substr( $tmp, strpos( $tmp, $eol ) + $add, $len );
			$tmp = substr( $tmp, strpos( $tmp, $eol ) + $add + $len );
		}
		return $str;
	}
}
?>