blob: 02033846ce439fcccc7c3340a20408a60d0b5941 [file] [log] [blame]
Mark Slee6e536442006-06-30 18:28:50 +00001<?php
David Reissea2cba82009-03-30 21:35:00 +00002/*
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
Mark Slee4902c052007-03-01 00:31:30 +000010 *
David Reissea2cba82009-03-30 21:35:00 +000011 * 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.
Mark Slee4902c052007-03-01 00:31:30 +000019 *
20 * @package thrift.transport
Mark Slee4902c052007-03-01 00:31:30 +000021 */
22
David Reissea2cba82009-03-30 21:35:00 +000023
Mark Slee4902c052007-03-01 00:31:30 +000024/**
Mark Slee6e536442006-06-30 18:28:50 +000025 * Sockets implementation of the TTransport interface.
26 *
27 * @package thrift.transport
Mark Slee6e536442006-06-30 18:28:50 +000028 */
29class TSocket extends TTransport {
30
31 /**
32 * Handle to PHP socket
33 *
34 * @var resource
35 */
36 private $handle_ = null;
37
38 /**
39 * Remote hostname
Mark Slee0cdc6c82007-11-13 10:19:08 +000040 *
Mark Slee6e536442006-06-30 18:28:50 +000041 * @var string
42 */
Mark Sleeade2c832006-09-08 03:41:50 +000043 protected $host_ = 'localhost';
Mark Slee6e536442006-06-30 18:28:50 +000044
45 /**
46 * Remote port
47 *
48 * @var int
49 */
Mark Sleeade2c832006-09-08 03:41:50 +000050 protected $port_ = '9090';
51
52 /**
Bryan Duxburyc46f32c2011-03-04 01:25:17 +000053 * Send timeout in seconds.
54 *
55 * Combined with sendTimeoutUsec this is used for send timeouts.
Mark Sleeade2c832006-09-08 03:41:50 +000056 *
57 * @var int
58 */
Bryan Duxburyc46f32c2011-03-04 01:25:17 +000059 private $sendTimeoutSec_ = 0;
Mark Sleeade2c832006-09-08 03:41:50 +000060
61 /**
Bryan Duxburyc46f32c2011-03-04 01:25:17 +000062 * Send timeout in microseconds.
63 *
64 * Combined with sendTimeoutSec this is used for send timeouts.
Mark Sleeade2c832006-09-08 03:41:50 +000065 *
66 * @var int
67 */
Bryan Duxburyc46f32c2011-03-04 01:25:17 +000068 private $sendTimeoutUsec_ = 100000;
Mark Sleeade2c832006-09-08 03:41:50 +000069
70 /**
Bryan Duxburyc46f32c2011-03-04 01:25:17 +000071 * Recv timeout in seconds
Mark Sleeade2c832006-09-08 03:41:50 +000072 *
Bryan Duxburyc46f32c2011-03-04 01:25:17 +000073 * Combined with recvTimeoutUsec this is used for recv timeouts.
74 *
75 * @var int
Mark Sleeade2c832006-09-08 03:41:50 +000076 */
Bryan Duxburyc46f32c2011-03-04 01:25:17 +000077 private $recvTimeoutSec_ = 0;
78
79 /**
80 * Recv timeout in microseconds
81 *
82 * Combined with recvTimeoutSec this is used for recv timeouts.
83 *
84 * @var int
85 */
86 private $recvTimeoutUsec_ = 750000;
Mark Slee6e536442006-06-30 18:28:50 +000087
88 /**
89 * Persistent socket or plain?
90 *
91 * @var bool
92 */
Jake Farrell10ed8e82011-04-13 19:42:35 +000093 protected $persist_ = FALSE;
Mark Sleeade2c832006-09-08 03:41:50 +000094
95 /**
96 * Debugging on?
97 *
98 * @var bool
99 */
robertb0fac3e2007-01-15 23:53:25 +0000100 protected $debug_ = FALSE;
Mark Slee6e536442006-06-30 18:28:50 +0000101
102 /**
Mark Sleead58f952007-01-03 19:23:50 +0000103 * Debug handler
104 *
105 * @var mixed
106 */
robertb0fac3e2007-01-15 23:53:25 +0000107 protected $debugHandler_ = null;
Mark Sleead58f952007-01-03 19:23:50 +0000108
109 /**
Mark Slee6e536442006-06-30 18:28:50 +0000110 * Socket constructor
111 *
Mark Sleead58f952007-01-03 19:23:50 +0000112 * @param string $host Remote hostname
113 * @param int $port Remote port
114 * @param bool $persist Whether to use a persistent socket
115 * @param string $debugHandler Function to call for error logging
Mark Slee6e536442006-06-30 18:28:50 +0000116 */
Mark Sleead58f952007-01-03 19:23:50 +0000117 public function __construct($host='localhost',
118 $port=9090,
119 $persist=FALSE,
120 $debugHandler=null) {
Mark Slee6e536442006-06-30 18:28:50 +0000121 $this->host_ = $host;
122 $this->port_ = $port;
123 $this->persist_ = $persist;
Mark Sleead58f952007-01-03 19:23:50 +0000124 $this->debugHandler_ = $debugHandler ? $debugHandler : 'error_log';
Mark Slee6e536442006-06-30 18:28:50 +0000125 }
126
127 /**
Bryan Duxbury17115d72010-08-12 16:59:19 +0000128 * @param resource $handle
129 * @return void
130 */
131 public function setHandle($handle) {
132 $this->handle_ = $handle;
133 }
134
135 /**
Mark Sleeade2c832006-09-08 03:41:50 +0000136 * Sets the send timeout.
137 *
David Reiss5ab303c2009-03-26 04:27:47 +0000138 * @param int $timeout Timeout in milliseconds.
Mark Sleeade2c832006-09-08 03:41:50 +0000139 */
140 public function setSendTimeout($timeout) {
Bryan Duxburyc46f32c2011-03-04 01:25:17 +0000141 $this->sendTimeoutSec_ = floor($timeout / 1000);
142 $this->sendTimeoutUsec_ =
143 ($timeout - ($this->sendTimeoutSec_ * 1000)) * 1000;
Mark Sleeade2c832006-09-08 03:41:50 +0000144 }
145
146 /**
147 * Sets the receive timeout.
148 *
David Reiss5ab303c2009-03-26 04:27:47 +0000149 * @param int $timeout Timeout in milliseconds.
Mark Sleeade2c832006-09-08 03:41:50 +0000150 */
151 public function setRecvTimeout($timeout) {
Bryan Duxburyc46f32c2011-03-04 01:25:17 +0000152 $this->recvTimeoutSec_ = floor($timeout / 1000);
153 $this->recvTimeoutUsec_ =
154 ($timeout - ($this->recvTimeoutSec_ * 1000)) * 1000;
Mark Sleeade2c832006-09-08 03:41:50 +0000155 }
156
157 /**
158 * Sets debugging output on or off
159 *
160 * @param bool $debug
161 */
162 public function setDebug($debug) {
163 $this->debug_ = $debug;
164 }
165
166 /**
Mark Slee0cdc6c82007-11-13 10:19:08 +0000167 * Get the host that this socket is connected to
168 *
169 * @return string host
170 */
171 public function getHost() {
172 return $this->host_;
173 }
174
175 /**
176 * Get the remote port that this socket is connected to
177 *
178 * @return int port
179 */
180 public function getPort() {
181 return $this->port_;
182 }
183
184 /**
Mark Slee6e536442006-06-30 18:28:50 +0000185 * Tests whether this is open
186 *
187 * @return bool true if the socket is open
188 */
189 public function isOpen() {
190 return is_resource($this->handle_);
191 }
192
193 /**
194 * Connects the socket.
195 */
196 public function open() {
Bryan Duxbury17115d72010-08-12 16:59:19 +0000197 if ($this->isOpen()) {
198 throw new TTransportException('Socket already connected', TTransportException::ALREADY_OPEN);
199 }
200
201 if (empty($this->host_)) {
202 throw new TTransportException('Cannot open null host', TTransportException::NOT_OPEN);
203 }
204
205 if ($this->port_ <= 0) {
206 throw new TTransportException('Cannot open without port', TTransportException::NOT_OPEN);
207 }
Mark Slee0cdc6c82007-11-13 10:19:08 +0000208
Mark Slee6e536442006-06-30 18:28:50 +0000209 if ($this->persist_) {
Mark Sleed7cc1c42006-10-04 16:49:07 +0000210 $this->handle_ = @pfsockopen($this->host_,
211 $this->port_,
212 $errno,
213 $errstr,
Bryan Duxburyc46f32c2011-03-04 01:25:17 +0000214 $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000));
Mark Sleed7cc1c42006-10-04 16:49:07 +0000215 } else {
216 $this->handle_ = @fsockopen($this->host_,
Mark Sleeade2c832006-09-08 03:41:50 +0000217 $this->port_,
218 $errno,
219 $errstr,
Bryan Duxburyc46f32c2011-03-04 01:25:17 +0000220 $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000));
Mark Slee6e536442006-06-30 18:28:50 +0000221 }
Mark Sleeade2c832006-09-08 03:41:50 +0000222
223 // Connect failed?
224 if ($this->handle_ === FALSE) {
Martin Kraemer0b64e772007-02-07 22:39:58 +0000225 $error = 'TSocket: Could not connect to '.$this->host_.':'.$this->port_.' ('.$errstr.' ['.$errno.'])';
Mark Sleeade2c832006-09-08 03:41:50 +0000226 if ($this->debug_) {
Mark Sleee7714a62007-01-11 01:26:00 +0000227 call_user_func($this->debugHandler_, $error);
Mark Sleeade2c832006-09-08 03:41:50 +0000228 }
Mark Slee76791962007-03-14 02:47:35 +0000229 throw new TException($error);
Mark Slee6e536442006-06-30 18:28:50 +0000230 }
231 }
232
233 /**
Mark Sleeade2c832006-09-08 03:41:50 +0000234 * Closes the socket.
Mark Slee6e536442006-06-30 18:28:50 +0000235 */
236 public function close() {
237 if (!$this->persist_) {
Mark Sleeade2c832006-09-08 03:41:50 +0000238 @fclose($this->handle_);
239 $this->handle_ = null;
Mark Slee6e536442006-06-30 18:28:50 +0000240 }
241 }
Mark Slee0cdc6c82007-11-13 10:19:08 +0000242
Mark Slee6e536442006-06-30 18:28:50 +0000243 /**
Bryan Duxburyc46f32c2011-03-04 01:25:17 +0000244 * Read from the socket at most $len bytes.
Mark Sleeade2c832006-09-08 03:41:50 +0000245 *
Bryan Duxburyc46f32c2011-03-04 01:25:17 +0000246 * This method will not wait for all the requested data, it will return as
247 * soon as any data is received.
Mark Sleeade2c832006-09-08 03:41:50 +0000248 *
Bryan Duxburyc46f32c2011-03-04 01:25:17 +0000249 * @param int $len Maximum number of bytes to read.
Mark Sleeade2c832006-09-08 03:41:50 +0000250 * @return string Binary data
Mark Slee6e536442006-06-30 18:28:50 +0000251 */
252 public function read($len) {
Bryan Duxburyc46f32c2011-03-04 01:25:17 +0000253 $null = null;
254 $read = array($this->handle_);
255 $readable = @stream_select($read, $null, $null, $this->recvTimeoutSec_, $this->recvTimeoutUsec_);
256
257 if ($readable > 0) {
258 $data = @stream_socket_recvfrom($this->handle_, $len);
259 if ($data === false) {
260 throw new TTransportException('TSocket: Could not read '.$len.' bytes from '.
261 $this->host_.':'.$this->port_);
262 } elseif($data == '' && feof($this->handle_)) {
263 throw new TTransportException('TSocket read 0 bytes');
264 }
265
266 return $data;
267 } else if ($readable === 0) {
Bryan Duxbury17115d72010-08-12 16:59:19 +0000268 throw new TTransportException('TSocket: timed out reading '.$len.' bytes from '.
Mark Slee76791962007-03-14 02:47:35 +0000269 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000270 } else {
Bryan Duxbury17115d72010-08-12 16:59:19 +0000271 throw new TTransportException('TSocket: Could not read '.$len.' bytes from '.
Mark Slee76791962007-03-14 02:47:35 +0000272 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000273 }
Mark Slee6e536442006-06-30 18:28:50 +0000274 }
Mark Slee6e536442006-06-30 18:28:50 +0000275
276 /**
277 * Write to the socket.
Mark Sleeade2c832006-09-08 03:41:50 +0000278 *
279 * @param string $buf The data to write
Mark Slee6e536442006-06-30 18:28:50 +0000280 */
281 public function write($buf) {
Bryan Duxburyc46f32c2011-03-04 01:25:17 +0000282 $null = null;
283 $write = array($this->handle_);
284
285 // keep writing until all the data has been written
Jake Farrellb03a59c2011-11-29 16:45:51 +0000286 while (TStringFuncFactory::create()->strlen($buf) > 0) {
Bryan Duxburyc46f32c2011-03-04 01:25:17 +0000287 // wait for stream to become available for writing
288 $writable = @stream_select($null, $write, $null, $this->sendTimeoutSec_, $this->sendTimeoutUsec_);
289 if ($writable > 0) {
290 // write buffer to stream
291 $written = @stream_socket_sendto($this->handle_, $buf);
292 if ($written === -1 || $written === false) {
Jake Farrellb03a59c2011-11-29 16:45:51 +0000293 throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '.
Bryan Duxburyc46f32c2011-03-04 01:25:17 +0000294 $this->host_.':'.$this->port_);
295 }
296 // determine how much of the buffer is left to write
Jake Farrellb03a59c2011-11-29 16:45:51 +0000297 $buf = TStringFuncFactory::create()->substr($buf, $written);
Bryan Duxburyc46f32c2011-03-04 01:25:17 +0000298 } else if ($writable === 0) {
Jake Farrellb03a59c2011-11-29 16:45:51 +0000299 throw new TTransportException('TSocket: timed out writing '.TStringFuncFactory::create()->strlen($buf).' bytes from '.
Mark Slee76791962007-03-14 02:47:35 +0000300 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000301 } else {
Jake Farrellb03a59c2011-11-29 16:45:51 +0000302 throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '.
Mark Slee76791962007-03-14 02:47:35 +0000303 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000304 }
Mark Slee6e536442006-06-30 18:28:50 +0000305 }
Mark Slee6e536442006-06-30 18:28:50 +0000306 }
Mark Slee6e536442006-06-30 18:28:50 +0000307
308 /**
309 * Flush output to the socket.
Bryan Duxburyc46f32c2011-03-04 01:25:17 +0000310 *
311 * Since read(), readAll() and write() operate on the sockets directly,
312 * this is a no-op
313 *
314 * If you wish to have flushable buffering behaviour, wrap this TSocket
315 * in a TBufferedTransport.
Mark Slee6e536442006-06-30 18:28:50 +0000316 */
317 public function flush() {
Bryan Duxburyc46f32c2011-03-04 01:25:17 +0000318 // no-op
Mark Sleeade2c832006-09-08 03:41:50 +0000319 }
Mark Slee6e536442006-06-30 18:28:50 +0000320 }