blob: bbe19875376eb6e153b3a019984c01687075155e [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 /**
53 * Send timeout in milliseconds
54 *
55 * @var int
56 */
57 private $sendTimeout_ = 100;
58
59 /**
60 * Recv timeout in milliseconds
61 *
62 * @var int
63 */
64 private $recvTimeout_ = 750;
65
66 /**
67 * Is send timeout set?
68 *
69 * @var bool
70 */
71 private $sendTimeoutSet_ = FALSE;
Mark Slee6e536442006-06-30 18:28:50 +000072
73 /**
74 * Persistent socket or plain?
75 *
76 * @var bool
77 */
Mark Sleeade2c832006-09-08 03:41:50 +000078 private $persist_ = FALSE;
79
80 /**
81 * Debugging on?
82 *
83 * @var bool
84 */
robertb0fac3e2007-01-15 23:53:25 +000085 protected $debug_ = FALSE;
Mark Slee6e536442006-06-30 18:28:50 +000086
87 /**
Mark Sleead58f952007-01-03 19:23:50 +000088 * Debug handler
89 *
90 * @var mixed
91 */
robertb0fac3e2007-01-15 23:53:25 +000092 protected $debugHandler_ = null;
Mark Sleead58f952007-01-03 19:23:50 +000093
94 /**
Mark Slee6e536442006-06-30 18:28:50 +000095 * Socket constructor
96 *
Mark Sleead58f952007-01-03 19:23:50 +000097 * @param string $host Remote hostname
98 * @param int $port Remote port
99 * @param bool $persist Whether to use a persistent socket
100 * @param string $debugHandler Function to call for error logging
Mark Slee6e536442006-06-30 18:28:50 +0000101 */
Mark Sleead58f952007-01-03 19:23:50 +0000102 public function __construct($host='localhost',
103 $port=9090,
104 $persist=FALSE,
105 $debugHandler=null) {
Mark Slee6e536442006-06-30 18:28:50 +0000106 $this->host_ = $host;
107 $this->port_ = $port;
108 $this->persist_ = $persist;
Mark Sleead58f952007-01-03 19:23:50 +0000109 $this->debugHandler_ = $debugHandler ? $debugHandler : 'error_log';
Mark Slee6e536442006-06-30 18:28:50 +0000110 }
111
112 /**
Bryan Duxbury17115d72010-08-12 16:59:19 +0000113 * @param resource $handle
114 * @return void
115 */
116 public function setHandle($handle) {
117 $this->handle_ = $handle;
118 }
119
120 /**
Mark Sleeade2c832006-09-08 03:41:50 +0000121 * Sets the send timeout.
122 *
David Reiss5ab303c2009-03-26 04:27:47 +0000123 * @param int $timeout Timeout in milliseconds.
Mark Sleeade2c832006-09-08 03:41:50 +0000124 */
125 public function setSendTimeout($timeout) {
126 $this->sendTimeout_ = $timeout;
127 }
128
129 /**
130 * Sets the receive timeout.
131 *
David Reiss5ab303c2009-03-26 04:27:47 +0000132 * @param int $timeout Timeout in milliseconds.
Mark Sleeade2c832006-09-08 03:41:50 +0000133 */
134 public function setRecvTimeout($timeout) {
135 $this->recvTimeout_ = $timeout;
136 }
137
138 /**
139 * Sets debugging output on or off
140 *
141 * @param bool $debug
142 */
143 public function setDebug($debug) {
144 $this->debug_ = $debug;
145 }
146
147 /**
Mark Slee0cdc6c82007-11-13 10:19:08 +0000148 * Get the host that this socket is connected to
149 *
150 * @return string host
151 */
152 public function getHost() {
153 return $this->host_;
154 }
155
156 /**
157 * Get the remote port that this socket is connected to
158 *
159 * @return int port
160 */
161 public function getPort() {
162 return $this->port_;
163 }
164
165 /**
Mark Slee6e536442006-06-30 18:28:50 +0000166 * Tests whether this is open
167 *
168 * @return bool true if the socket is open
169 */
170 public function isOpen() {
171 return is_resource($this->handle_);
172 }
173
174 /**
175 * Connects the socket.
176 */
177 public function open() {
Bryan Duxbury17115d72010-08-12 16:59:19 +0000178 if ($this->isOpen()) {
179 throw new TTransportException('Socket already connected', TTransportException::ALREADY_OPEN);
180 }
181
182 if (empty($this->host_)) {
183 throw new TTransportException('Cannot open null host', TTransportException::NOT_OPEN);
184 }
185
186 if ($this->port_ <= 0) {
187 throw new TTransportException('Cannot open without port', TTransportException::NOT_OPEN);
188 }
Mark Slee0cdc6c82007-11-13 10:19:08 +0000189
Mark Slee6e536442006-06-30 18:28:50 +0000190 if ($this->persist_) {
Mark Sleed7cc1c42006-10-04 16:49:07 +0000191 $this->handle_ = @pfsockopen($this->host_,
192 $this->port_,
193 $errno,
194 $errstr,
195 $this->sendTimeout_/1000.0);
196 } else {
197 $this->handle_ = @fsockopen($this->host_,
Mark Sleeade2c832006-09-08 03:41:50 +0000198 $this->port_,
199 $errno,
200 $errstr,
201 $this->sendTimeout_/1000.0);
Mark Slee6e536442006-06-30 18:28:50 +0000202 }
Mark Sleeade2c832006-09-08 03:41:50 +0000203
204 // Connect failed?
205 if ($this->handle_ === FALSE) {
Martin Kraemer0b64e772007-02-07 22:39:58 +0000206 $error = 'TSocket: Could not connect to '.$this->host_.':'.$this->port_.' ('.$errstr.' ['.$errno.'])';
Mark Sleeade2c832006-09-08 03:41:50 +0000207 if ($this->debug_) {
Mark Sleee7714a62007-01-11 01:26:00 +0000208 call_user_func($this->debugHandler_, $error);
Mark Sleeade2c832006-09-08 03:41:50 +0000209 }
Mark Slee76791962007-03-14 02:47:35 +0000210 throw new TException($error);
Mark Slee6e536442006-06-30 18:28:50 +0000211 }
Mark Slee0cdc6c82007-11-13 10:19:08 +0000212
Mark Sleeade2c832006-09-08 03:41:50 +0000213 stream_set_timeout($this->handle_, 0, $this->sendTimeout_*1000);
214 $this->sendTimeoutSet_ = TRUE;
Mark Slee6e536442006-06-30 18:28:50 +0000215 }
216
217 /**
Mark Sleeade2c832006-09-08 03:41:50 +0000218 * Closes the socket.
Mark Slee6e536442006-06-30 18:28:50 +0000219 */
220 public function close() {
221 if (!$this->persist_) {
Mark Sleeade2c832006-09-08 03:41:50 +0000222 @fclose($this->handle_);
223 $this->handle_ = null;
Mark Slee6e536442006-06-30 18:28:50 +0000224 }
225 }
Mark Slee0cdc6c82007-11-13 10:19:08 +0000226
Mark Slee6e536442006-06-30 18:28:50 +0000227 /**
228 * Uses stream get contents to do the reading
Mark Sleeade2c832006-09-08 03:41:50 +0000229 *
230 * @param int $len How many bytes
231 * @return string Binary data
Mark Slee6e536442006-06-30 18:28:50 +0000232 */
233 public function readAll($len) {
Mark Sleeade2c832006-09-08 03:41:50 +0000234 if ($this->sendTimeoutSet_) {
235 stream_set_timeout($this->handle_, 0, $this->recvTimeout_*1000);
236 $this->sendTimeoutSet_ = FALSE;
237 }
Mark Slee794993d2006-09-20 01:56:10 +0000238 // This call does not obey stream_set_timeout values!
239 // $buf = @stream_get_contents($this->handle_, $len);
240
241 $pre = null;
Mark Slee29f5f672006-09-28 03:19:03 +0000242 while (TRUE) {
Mark Slee794993d2006-09-20 01:56:10 +0000243 $buf = @fread($this->handle_, $len);
Mark Sleee598d072006-11-21 02:01:22 +0000244 if ($buf === FALSE || $buf === '') {
Martin Kraemer0b64e772007-02-07 22:39:58 +0000245 $md = stream_get_meta_data($this->handle_);
246 if ($md['timed_out']) {
Bryan Duxbury17115d72010-08-12 16:59:19 +0000247 throw new TTransportException('TSocket: timed out reading '.$len.' bytes from '.
Mark Slee76791962007-03-14 02:47:35 +0000248 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000249 } else {
Bryan Duxbury17115d72010-08-12 16:59:19 +0000250 throw new TTransportException('TSocket: Could not read '.$len.' bytes from '.
Mark Slee76791962007-03-14 02:47:35 +0000251 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000252 }
Mark Slee794993d2006-09-20 01:56:10 +0000253 } else if (($sz = strlen($buf)) < $len) {
254 $md = stream_get_meta_data($this->handle_);
255 if ($md['timed_out']) {
Bryan Duxbury17115d72010-08-12 16:59:19 +0000256 throw new TTransportException('TSocket: timed out reading '.$len.' bytes from '.
Mark Slee76791962007-03-14 02:47:35 +0000257 $this->host_.':'.$this->port_);
Mark Slee794993d2006-09-20 01:56:10 +0000258 } else {
259 $pre .= $buf;
260 $len -= $sz;
261 }
262 } else {
263 return $pre.$buf;
264 }
Mark Sleeade2c832006-09-08 03:41:50 +0000265 }
Mark Slee6e536442006-06-30 18:28:50 +0000266 }
267
268 /**
269 * Read from the socket
Mark Sleeade2c832006-09-08 03:41:50 +0000270 *
271 * @param int $len How many bytes
272 * @return string Binary data
Mark Slee6e536442006-06-30 18:28:50 +0000273 */
274 public function read($len) {
Mark Sleeade2c832006-09-08 03:41:50 +0000275 if ($this->sendTimeoutSet_) {
276 stream_set_timeout($this->handle_, 0, $this->recvTimeout_*1000);
277 $this->sendTimeoutSet_ = FALSE;
278 }
Mark Slee794993d2006-09-20 01:56:10 +0000279 $data = @fread($this->handle_, $len);
Mark Sleee598d072006-11-21 02:01:22 +0000280 if ($data === FALSE || $data === '') {
Martin Kraemer0b64e772007-02-07 22:39:58 +0000281 $md = stream_get_meta_data($this->handle_);
282 if ($md['timed_out']) {
Bryan Duxbury17115d72010-08-12 16:59:19 +0000283 throw new TTransportException('TSocket: timed out reading '.$len.' bytes from '.
Mark Slee76791962007-03-14 02:47:35 +0000284 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000285 } else {
Bryan Duxbury17115d72010-08-12 16:59:19 +0000286 throw new TTransportException('TSocket: Could not read '.$len.' bytes from '.
Mark Slee76791962007-03-14 02:47:35 +0000287 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000288 }
Mark Slee6e536442006-06-30 18:28:50 +0000289 }
290 return $data;
291 }
292
293 /**
294 * Write to the socket.
Mark Sleeade2c832006-09-08 03:41:50 +0000295 *
296 * @param string $buf The data to write
Mark Slee6e536442006-06-30 18:28:50 +0000297 */
298 public function write($buf) {
Mark Sleeade2c832006-09-08 03:41:50 +0000299 if (!$this->sendTimeoutSet_) {
300 stream_set_timeout($this->handle_, 0, $this->sendTimeout_*1000);
301 $this->sendTimeoutSet_ = TRUE;
302 }
Mark Sleed395d572007-02-27 01:16:55 +0000303 while (strlen($buf) > 0) {
Mark Sleeade2c832006-09-08 03:41:50 +0000304 $got = @fwrite($this->handle_, $buf);
305 if ($got === 0 || $got === FALSE) {
Martin Kraemer0b64e772007-02-07 22:39:58 +0000306 $md = stream_get_meta_data($this->handle_);
307 if ($md['timed_out']) {
Bryan Duxbury17115d72010-08-12 16:59:19 +0000308 throw new TTransportException('TSocket: timed out writing '.strlen($buf).' bytes from '.
Mark Slee76791962007-03-14 02:47:35 +0000309 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000310 } else {
Bryan Duxbury17115d72010-08-12 16:59:19 +0000311 throw new TTransportException('TSocket: Could not write '.strlen($buf).' bytes '.
Mark Slee76791962007-03-14 02:47:35 +0000312 $this->host_.':'.$this->port_);
Martin Kraemer0b64e772007-02-07 22:39:58 +0000313 }
Mark Slee6e536442006-06-30 18:28:50 +0000314 }
315 $buf = substr($buf, $got);
316 }
317 }
318
319 /**
320 * Flush output to the socket.
321 */
322 public function flush() {
Mark Sleeade2c832006-09-08 03:41:50 +0000323 $ret = fflush($this->handle_);
324 if ($ret === FALSE) {
Mark Slee76791962007-03-14 02:47:35 +0000325 throw new TException('TSocket: Could not flush: '.
326 $this->host_.':'.$this->port_);
Mark Sleeade2c832006-09-08 03:41:50 +0000327 }
Mark Slee6e536442006-06-30 18:28:50 +0000328 }
329}