1<?php 2/* 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 * @package thrift.transport 21 */ 22 23namespace Thrift\Transport; 24 25use Thrift\Exception\TTransportException; 26use Thrift\Factory\TStringFuncFactory; 27 28/** 29 * HTTP client for Thrift 30 * 31 * @package thrift.transport 32 */ 33class THttpClient extends TTransport 34{ 35 /** 36 * The host to connect to 37 * 38 * @var string 39 */ 40 protected $host_; 41 42 /** 43 * The port to connect on 44 * 45 * @var int 46 */ 47 protected $port_; 48 49 /** 50 * The URI to request 51 * 52 * @var string 53 */ 54 protected $uri_; 55 56 /** 57 * The scheme to use for the request, i.e. http, https 58 * 59 * @var string 60 */ 61 protected $scheme_; 62 63 /** 64 * Buffer for the HTTP request data 65 * 66 * @var string 67 */ 68 protected $buf_; 69 70 /** 71 * Input socket stream. 72 * 73 * @var resource 74 */ 75 protected $handle_; 76 77 /** 78 * Read timeout 79 * 80 * @var float 81 */ 82 protected $timeout_; 83 84 /** 85 * http headers 86 * 87 * @var array 88 */ 89 protected $headers_; 90 91 /** 92 * Context additional options 93 * 94 * @var array 95 */ 96 protected $context_; 97 98 /** 99 * Make a new HTTP client. 100 * 101 * @param string $host 102 * @param int $port 103 * @param string $uri 104 * @param string $scheme 105 * @param array $context 106 */ 107 public function __construct($host, $port = 80, $uri = '', $scheme = 'http', array $context = array()) 108 { 109 if ((TStringFuncFactory::create()->strlen($uri) > 0) && ($uri[0] != '/')) { 110 $uri = '/' . $uri; 111 } 112 $this->scheme_ = $scheme; 113 $this->host_ = $host; 114 $this->port_ = $port; 115 $this->uri_ = $uri; 116 $this->buf_ = ''; 117 $this->handle_ = null; 118 $this->timeout_ = null; 119 $this->headers_ = array(); 120 $this->context_ = $context; 121 } 122 123 /** 124 * Set read timeout 125 * 126 * @param float $timeout 127 */ 128 public function setTimeoutSecs($timeout) 129 { 130 $this->timeout_ = $timeout; 131 } 132 133 /** 134 * Whether this transport is open. 135 * 136 * @return boolean true if open 137 */ 138 public function isOpen() 139 { 140 return true; 141 } 142 143 /** 144 * Open the transport for reading/writing 145 * 146 * @throws TTransportException if cannot open 147 */ 148 public function open() 149 { 150 } 151 152 /** 153 * Close the transport. 154 */ 155 public function close() 156 { 157 if ($this->handle_) { 158 @fclose($this->handle_); 159 $this->handle_ = null; 160 } 161 } 162 163 /** 164 * Read some data into the array. 165 * 166 * @param int $len How much to read 167 * @return string The data that has been read 168 * @throws TTransportException if cannot read any more data 169 */ 170 public function read($len) 171 { 172 $data = @fread($this->handle_, $len); 173 if ($data === false || $data === '') { 174 $md = stream_get_meta_data($this->handle_); 175 if ($md['timed_out']) { 176 throw new TTransportException( 177 'THttpClient: timed out reading ' . $len . ' bytes from ' . 178 $this->host_ . ':' . $this->port_ . $this->uri_, 179 TTransportException::TIMED_OUT 180 ); 181 } else { 182 throw new TTransportException( 183 'THttpClient: Could not read ' . $len . ' bytes from ' . 184 $this->host_ . ':' . $this->port_ . $this->uri_, 185 TTransportException::UNKNOWN 186 ); 187 } 188 } 189 190 return $data; 191 } 192 193 /** 194 * Writes some data into the pending buffer 195 * 196 * @param string $buf The data to write 197 * @throws TTransportException if writing fails 198 */ 199 public function write($buf) 200 { 201 $this->buf_ .= $buf; 202 } 203 204 /** 205 * Opens and sends the actual request over the HTTP connection 206 * 207 * @throws TTransportException if a writing error occurs 208 */ 209 public function flush() 210 { 211 // God, PHP really has some esoteric ways of doing simple things. 212 $host = $this->host_ . ($this->port_ != 80 ? ':' . $this->port_ : ''); 213 214 $headers = array(); 215 $defaultHeaders = array('Host' => $host, 216 'Accept' => 'application/x-thrift', 217 'User-Agent' => 'PHP/THttpClient', 218 'Content-Type' => 'application/x-thrift', 219 'Content-Length' => TStringFuncFactory::create()->strlen($this->buf_)); 220 foreach (array_merge($defaultHeaders, $this->headers_) as $key => $value) { 221 $headers[] = "$key: $value"; 222 } 223 224 $options = $this->context_; 225 226 $baseHttpOptions = isset($options["http"]) ? $options["http"] : array(); 227 228 $httpOptions = $baseHttpOptions + array('method' => 'POST', 229 'header' => implode("\r\n", $headers), 230 'max_redirects' => 1, 231 'content' => $this->buf_); 232 if ($this->timeout_ > 0) { 233 $httpOptions['timeout'] = $this->timeout_; 234 } 235 $this->buf_ = ''; 236 237 $options["http"] = $httpOptions; 238 $contextid = stream_context_create($options); 239 $this->handle_ = @fopen( 240 $this->scheme_ . '://' . $host . $this->uri_, 241 'r', 242 false, 243 $contextid 244 ); 245 246 // Connect failed? 247 if ($this->handle_ === false) { 248 $this->handle_ = null; 249 $error = 'THttpClient: Could not connect to ' . $host . $this->uri_; 250 throw new TTransportException($error, TTransportException::NOT_OPEN); 251 } 252 } 253 254 public function addHeaders($headers) 255 { 256 $this->headers_ = array_merge($this->headers_, $headers); 257 } 258} 259