1<?php
2
3/**
4 * Copyright (c) 2006- Facebook
5 * Distributed under the Thrift Software License
6 *
7 * See accompanying file LICENSE or visit the Thrift site at:
8 * http://developers.facebook.com/thrift/
9 *
10 * @package thrift.transport
11 * @author Mark Slee <mcslee@facebook.com>
12 */
13
14/**
15 * HTTP client for Thrift
16 *
17 * @package thrift.transport
18 * @author Mark Slee <mcslee@facebook.com>
19 */
20class THttpClient extends TTransport {
21
22  /**
23   * The host to connect to
24   *
25   * @var string
26   */
27  protected $host_;
28
29  /**
30   * The port to connect on
31   *
32   * @var int
33   */
34  protected $port_;
35
36  /**
37   * The URI to request
38   *
39   * @var string
40   */
41  protected $uri_;
42
43  /**
44   * The scheme to use for the request, i.e. http, https
45   *
46   * @var string
47   */
48  protected $scheme_;
49
50  /**
51   * Buffer for the HTTP request data
52   *
53   * @var string
54   */
55  protected $buf_;
56
57  /**
58   * Input socket stream.
59   *
60   * @var resource
61   */
62  protected $handle_;
63
64  /**
65   * Read timeout
66   *
67   * @var float
68   */
69  protected $timeout_;
70
71  /**
72   * Make a new HTTP client.
73   *
74   * @param string $host
75   * @param int    $port
76   * @param string $uri
77   */
78  public function __construct($host, $port=80, $uri='', $scheme = 'http') {
79    if ((strlen($uri) > 0) && ($uri{0} != '/')) {
80      $uri = '/'.$uri;
81    }
82    $this->scheme_ = $scheme;
83    $this->host_ = $host;
84    $this->port_ = $port;
85    $this->uri_ = $uri;
86    $this->buf_ = '';
87    $this->handle_ = null;
88    $this->timeout_ = null;
89  }
90
91  /**
92   * Set read timeout
93   *
94   * @param float $timeout
95   */
96  public function setTimeoutSecs($timeout) {
97    $this->timeout_ = $timeout;
98  }
99
100  /**
101   * Whether this transport is open.
102   *
103   * @return boolean true if open
104   */
105  public function isOpen() {
106    return true;
107  }
108
109  /**
110   * Open the transport for reading/writing
111   *
112   * @throws TTransportException if cannot open
113   */
114  public function open() {}
115
116  /**
117   * Close the transport.
118   */
119  public function close() {
120    if ($this->handle_) {
121      @fclose($this->handle_);
122      $this->handle_ = null;
123    }
124  }
125
126  /**
127   * Read some data into the array.
128   *
129   * @param int    $len How much to read
130   * @return string The data that has been read
131   * @throws TTransportException if cannot read any more data
132   */
133  public function read($len) {
134    $data = @fread($this->handle_, $len);
135    if ($data === FALSE || $data === '') {
136      $md = stream_get_meta_data($this->handle_);
137      if ($md['timed_out']) {
138        throw new TTransportException('THttpClient: timed out reading '.$len.' bytes from '.$this->host_.':'.$this->port_.'/'.$this->uri_, TTransportException::TIMED_OUT);
139      } else {
140        throw new TTransportException('THttpClient: Could not read '.$len.' bytes from '.$this->host_.':'.$this->port_.'/'.$this->uri_, TTransportException::UNKNOWN);
141      }
142    }
143    return $data;
144  }
145
146  /**
147   * Writes some data into the pending buffer
148   *
149   * @param string $buf  The data to write
150   * @throws TTransportException if writing fails
151   */
152  public function write($buf) {
153    $this->buf_ .= $buf;
154  }
155
156  /**
157   * Opens and sends the actual request over the HTTP connection
158   *
159   * @throws TTransportException if a writing error occurs
160   */
161  public function flush() {
162    // God, PHP really has some esoteric ways of doing simple things.
163    $host = $this->host_.($this->port_ != 80 ? ':'.$this->port_ : '');
164
165    $headers = array('Host: '.$host,
166                     'Accept: application/x-thrift',
167                     'User-Agent: PHP/THttpClient',
168                     'Content-Type: application/x-thrift',
169                     'Content-Length: '.strlen($this->buf_));
170
171    $options = array('method' => 'POST',
172                     'header' => implode("\r\n", $headers),
173                     'max_redirects' => 1,
174                     'content' => $this->buf_);
175    if ($this->timeout_ > 0) {
176      $options['timeout'] = $this->timeout_;
177    }
178    $this->buf_ = '';
179
180    $contextid = stream_context_create(array('http' => $options));
181    $this->handle_ = @fopen($this->scheme_.'://'.$host.$this->uri_, 'r', false, $contextid);
182
183    // Connect failed?
184    if ($this->handle_ === FALSE) {
185      $this->handle_ = null;
186      $error = 'THttpClient: Could not connect to '.$host.$this->uri_;
187      throw new TTransportException($error, TTransportException::NOT_OPEN);
188    }
189  }
190
191}
192
193?>
194