Statistics
| Branch: | Tag: | Revision:

root / lib / http.py @ a2d2e1a7

History | View | Annotate | Download (40.9 kB)

1 a43f68dc Michael Hanselmann
#
2 a43f68dc Michael Hanselmann
#
3 a43f68dc Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
4 a43f68dc Michael Hanselmann
# it under the terms of the GNU General Public License as published by
5 a43f68dc Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
6 a43f68dc Michael Hanselmann
# (at your option) any later version.
7 a43f68dc Michael Hanselmann
#
8 a43f68dc Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
9 a43f68dc Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
10 a43f68dc Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 a43f68dc Michael Hanselmann
# General Public License for more details.
12 a43f68dc Michael Hanselmann
#
13 a43f68dc Michael Hanselmann
# You should have received a copy of the GNU General Public License
14 a43f68dc Michael Hanselmann
# along with this program; if not, write to the Free Software
15 a43f68dc Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 a43f68dc Michael Hanselmann
# 02110-1301, USA.
17 a43f68dc Michael Hanselmann
18 a43f68dc Michael Hanselmann
"""HTTP server module.
19 a43f68dc Michael Hanselmann

20 a43f68dc Michael Hanselmann
"""
21 a43f68dc Michael Hanselmann
22 a43f68dc Michael Hanselmann
import BaseHTTPServer
23 42242313 Michael Hanselmann
import cgi
24 42242313 Michael Hanselmann
import logging
25 42242313 Michael Hanselmann
import mimetools
26 a43f68dc Michael Hanselmann
import OpenSSL
27 42242313 Michael Hanselmann
import os
28 42242313 Michael Hanselmann
import select
29 42242313 Michael Hanselmann
import socket
30 42242313 Michael Hanselmann
import sys
31 a43f68dc Michael Hanselmann
import time
32 42242313 Michael Hanselmann
import signal
33 263ab7cf Iustin Pop
import logging
34 8a0b06d2 Michael Hanselmann
import errno
35 33bbdbec Michael Hanselmann
import threading
36 8a0b06d2 Michael Hanselmann
37 8a0b06d2 Michael Hanselmann
from cStringIO import StringIO
38 a43f68dc Michael Hanselmann
39 42242313 Michael Hanselmann
from ganeti import constants
40 a43f68dc Michael Hanselmann
from ganeti import serializer
41 8a0b06d2 Michael Hanselmann
from ganeti import workerpool
42 f2a6fc9e Michael Hanselmann
from ganeti import utils
43 a43f68dc Michael Hanselmann
44 a43f68dc Michael Hanselmann
45 8a0b06d2 Michael Hanselmann
HTTP_CLIENT_THREADS = 10
46 8a0b06d2 Michael Hanselmann
47 8a9f9060 Michael Hanselmann
HTTP_GANETI_VERSION = "Ganeti %s" % constants.RELEASE_VERSION
48 8a9f9060 Michael Hanselmann
49 42242313 Michael Hanselmann
WEEKDAYNAME = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
50 42242313 Michael Hanselmann
MONTHNAME = [None,
51 42242313 Michael Hanselmann
             'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
52 42242313 Michael Hanselmann
             'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
53 42242313 Michael Hanselmann
54 42242313 Michael Hanselmann
# Default error message
55 42242313 Michael Hanselmann
DEFAULT_ERROR_CONTENT_TYPE = "text/html"
56 42242313 Michael Hanselmann
DEFAULT_ERROR_MESSAGE = """\
57 42242313 Michael Hanselmann
<head>
58 42242313 Michael Hanselmann
<title>Error response</title>
59 42242313 Michael Hanselmann
</head>
60 42242313 Michael Hanselmann
<body>
61 42242313 Michael Hanselmann
<h1>Error response</h1>
62 42242313 Michael Hanselmann
<p>Error code %(code)d.
63 42242313 Michael Hanselmann
<p>Message: %(message)s.
64 42242313 Michael Hanselmann
<p>Error code explanation: %(code)s = %(explain)s.
65 42242313 Michael Hanselmann
</body>
66 42242313 Michael Hanselmann
"""
67 42242313 Michael Hanselmann
68 42242313 Michael Hanselmann
HTTP_OK = 200
69 42242313 Michael Hanselmann
HTTP_NO_CONTENT = 204
70 42242313 Michael Hanselmann
HTTP_NOT_MODIFIED = 304
71 42242313 Michael Hanselmann
72 42242313 Michael Hanselmann
HTTP_0_9 = "HTTP/0.9"
73 42242313 Michael Hanselmann
HTTP_1_0 = "HTTP/1.0"
74 42242313 Michael Hanselmann
HTTP_1_1 = "HTTP/1.1"
75 42242313 Michael Hanselmann
76 42242313 Michael Hanselmann
HTTP_GET = "GET"
77 42242313 Michael Hanselmann
HTTP_HEAD = "HEAD"
78 8a9f9060 Michael Hanselmann
HTTP_POST = "POST"
79 8a9f9060 Michael Hanselmann
HTTP_PUT = "PUT"
80 8a9f9060 Michael Hanselmann
81 713faea6 Oleksiy Mishchenko
HTTP_ETAG = "ETag"
82 8a9f9060 Michael Hanselmann
HTTP_HOST = "Host"
83 8a9f9060 Michael Hanselmann
HTTP_SERVER = "Server"
84 8a9f9060 Michael Hanselmann
HTTP_DATE = "Date"
85 8a9f9060 Michael Hanselmann
HTTP_USER_AGENT = "User-Agent"
86 8a9f9060 Michael Hanselmann
HTTP_CONTENT_TYPE = "Content-Type"
87 8a9f9060 Michael Hanselmann
HTTP_CONTENT_LENGTH = "Content-Length"
88 8a9f9060 Michael Hanselmann
HTTP_CONNECTION = "Connection"
89 8a9f9060 Michael Hanselmann
HTTP_KEEP_ALIVE = "Keep-Alive"
90 42242313 Michael Hanselmann
91 d7bace1b Michael Hanselmann
_SSL_UNEXPECTED_EOF = "Unexpected EOF"
92 d7bace1b Michael Hanselmann
93 42242313 Michael Hanselmann
94 42242313 Michael Hanselmann
class SocketClosed(socket.error):
95 42242313 Michael Hanselmann
  pass
96 42242313 Michael Hanselmann
97 42242313 Michael Hanselmann
98 438a366a Michael Hanselmann
class _HttpClientError(Exception):
99 438a366a Michael Hanselmann
  """Internal exception for HTTP client errors.
100 438a366a Michael Hanselmann

101 438a366a Michael Hanselmann
  This should only be used for internal error reporting.
102 438a366a Michael Hanselmann

103 438a366a Michael Hanselmann
  """
104 8a0b06d2 Michael Hanselmann
  pass
105 8a0b06d2 Michael Hanselmann
106 8a0b06d2 Michael Hanselmann
107 a43f68dc Michael Hanselmann
class HTTPException(Exception):
108 a43f68dc Michael Hanselmann
  code = None
109 a43f68dc Michael Hanselmann
  message = None
110 a43f68dc Michael Hanselmann
111 a43f68dc Michael Hanselmann
  def __init__(self, message=None):
112 42242313 Michael Hanselmann
    Exception.__init__(self)
113 a43f68dc Michael Hanselmann
    if message is not None:
114 a43f68dc Michael Hanselmann
      self.message = message
115 a43f68dc Michael Hanselmann
116 a43f68dc Michael Hanselmann
117 a43f68dc Michael Hanselmann
class HTTPBadRequest(HTTPException):
118 a43f68dc Michael Hanselmann
  code = 400
119 a43f68dc Michael Hanselmann
120 a43f68dc Michael Hanselmann
121 a43f68dc Michael Hanselmann
class HTTPForbidden(HTTPException):
122 a43f68dc Michael Hanselmann
  code = 403
123 a43f68dc Michael Hanselmann
124 a43f68dc Michael Hanselmann
125 a43f68dc Michael Hanselmann
class HTTPNotFound(HTTPException):
126 a43f68dc Michael Hanselmann
  code = 404
127 a43f68dc Michael Hanselmann
128 a43f68dc Michael Hanselmann
129 a43f68dc Michael Hanselmann
class HTTPGone(HTTPException):
130 a43f68dc Michael Hanselmann
  code = 410
131 a43f68dc Michael Hanselmann
132 a43f68dc Michael Hanselmann
133 a43f68dc Michael Hanselmann
class HTTPLengthRequired(HTTPException):
134 a43f68dc Michael Hanselmann
  code = 411
135 a43f68dc Michael Hanselmann
136 a43f68dc Michael Hanselmann
137 a43f68dc Michael Hanselmann
class HTTPInternalError(HTTPException):
138 a43f68dc Michael Hanselmann
  code = 500
139 a43f68dc Michael Hanselmann
140 a43f68dc Michael Hanselmann
141 a43f68dc Michael Hanselmann
class HTTPNotImplemented(HTTPException):
142 a43f68dc Michael Hanselmann
  code = 501
143 a43f68dc Michael Hanselmann
144 a43f68dc Michael Hanselmann
145 a43f68dc Michael Hanselmann
class HTTPServiceUnavailable(HTTPException):
146 a43f68dc Michael Hanselmann
  code = 503
147 a43f68dc Michael Hanselmann
148 a43f68dc Michael Hanselmann
149 42242313 Michael Hanselmann
class HTTPVersionNotSupported(HTTPException):
150 42242313 Michael Hanselmann
  code = 505
151 42242313 Michael Hanselmann
152 42242313 Michael Hanselmann
153 a43f68dc Michael Hanselmann
class ApacheLogfile:
154 a43f68dc Michael Hanselmann
  """Utility class to write HTTP server log files.
155 a43f68dc Michael Hanselmann

156 a43f68dc Michael Hanselmann
  The written format is the "Common Log Format" as defined by Apache:
157 a43f68dc Michael Hanselmann
  http://httpd.apache.org/docs/2.2/mod/mod_log_config.html#examples
158 a43f68dc Michael Hanselmann

159 a43f68dc Michael Hanselmann
  """
160 a43f68dc Michael Hanselmann
  def __init__(self, fd):
161 a43f68dc Michael Hanselmann
    """Constructor for ApacheLogfile class.
162 a43f68dc Michael Hanselmann

163 a43f68dc Michael Hanselmann
    Args:
164 a43f68dc Michael Hanselmann
    - fd: Open file object
165 a43f68dc Michael Hanselmann

166 a43f68dc Michael Hanselmann
    """
167 a43f68dc Michael Hanselmann
    self._fd = fd
168 a43f68dc Michael Hanselmann
169 a43f68dc Michael Hanselmann
  def LogRequest(self, request, format, *args):
170 a43f68dc Michael Hanselmann
    self._fd.write("%s %s %s [%s] %s\n" % (
171 a43f68dc Michael Hanselmann
      # Remote host address
172 a43f68dc Michael Hanselmann
      request.address_string(),
173 a43f68dc Michael Hanselmann
174 a43f68dc Michael Hanselmann
      # RFC1413 identity (identd)
175 a43f68dc Michael Hanselmann
      "-",
176 a43f68dc Michael Hanselmann
177 a43f68dc Michael Hanselmann
      # Remote user
178 a43f68dc Michael Hanselmann
      "-",
179 a43f68dc Michael Hanselmann
180 a43f68dc Michael Hanselmann
      # Request time
181 a43f68dc Michael Hanselmann
      self._FormatCurrentTime(),
182 a43f68dc Michael Hanselmann
183 a43f68dc Michael Hanselmann
      # Message
184 a43f68dc Michael Hanselmann
      format % args,
185 a43f68dc Michael Hanselmann
      ))
186 a0638838 Oleksiy Mishchenko
    self._fd.flush()
187 a43f68dc Michael Hanselmann
188 a43f68dc Michael Hanselmann
  def _FormatCurrentTime(self):
189 a43f68dc Michael Hanselmann
    """Formats current time in Common Log Format.
190 a43f68dc Michael Hanselmann

191 a43f68dc Michael Hanselmann
    """
192 a43f68dc Michael Hanselmann
    return self._FormatLogTime(time.time())
193 a43f68dc Michael Hanselmann
194 a43f68dc Michael Hanselmann
  def _FormatLogTime(self, seconds):
195 a43f68dc Michael Hanselmann
    """Formats time for Common Log Format.
196 a43f68dc Michael Hanselmann

197 a43f68dc Michael Hanselmann
    All timestamps are logged in the UTC timezone.
198 a43f68dc Michael Hanselmann

199 a43f68dc Michael Hanselmann
    Args:
200 a43f68dc Michael Hanselmann
    - seconds: Time in seconds since the epoch
201 a43f68dc Michael Hanselmann

202 a43f68dc Michael Hanselmann
    """
203 a43f68dc Michael Hanselmann
    (_, month, _, _, _, _, _, _, _) = tm = time.gmtime(seconds)
204 42242313 Michael Hanselmann
    format = "%d/" + MONTHNAME[month] + "/%Y:%H:%M:%S +0000"
205 a43f68dc Michael Hanselmann
    return time.strftime(format, tm)
206 a43f68dc Michael Hanselmann
207 a43f68dc Michael Hanselmann
208 a43f68dc Michael Hanselmann
class HTTPJsonConverter:
209 a43f68dc Michael Hanselmann
  CONTENT_TYPE = "application/json"
210 a43f68dc Michael Hanselmann
211 a43f68dc Michael Hanselmann
  def Encode(self, data):
212 a43f68dc Michael Hanselmann
    return serializer.DumpJson(data)
213 a43f68dc Michael Hanselmann
214 a43f68dc Michael Hanselmann
  def Decode(self, data):
215 a43f68dc Michael Hanselmann
    return serializer.LoadJson(data)
216 a43f68dc Michael Hanselmann
217 a43f68dc Michael Hanselmann
218 f20cbea2 Michael Hanselmann
class HttpSslParams(object):
219 f20cbea2 Michael Hanselmann
  """Data class for SSL key and certificate.
220 f20cbea2 Michael Hanselmann

221 f20cbea2 Michael Hanselmann
  """
222 f20cbea2 Michael Hanselmann
  def __init__(self, ssl_key_path, ssl_cert_path):
223 f20cbea2 Michael Hanselmann
    """Initializes this class.
224 f20cbea2 Michael Hanselmann

225 f20cbea2 Michael Hanselmann
    @type ssl_key_path: string
226 f20cbea2 Michael Hanselmann
    @param ssl_key_path: Path to file containing SSL key in PEM format
227 f20cbea2 Michael Hanselmann
    @type ssl_cert_path: string
228 f20cbea2 Michael Hanselmann
    @param ssl_cert_path: Path to file containing SSL certificate in PEM format
229 f20cbea2 Michael Hanselmann

230 f20cbea2 Michael Hanselmann
    """
231 f20cbea2 Michael Hanselmann
    ssl_key_pem = utils.ReadFile(ssl_key_path)
232 f20cbea2 Michael Hanselmann
    ssl_cert_pem = utils.ReadFile(ssl_cert_path)
233 f20cbea2 Michael Hanselmann
234 f20cbea2 Michael Hanselmann
    cr = OpenSSL.crypto
235 f20cbea2 Michael Hanselmann
    self.cert = cr.load_certificate(cr.FILETYPE_PEM, ssl_cert_pem)
236 f20cbea2 Michael Hanselmann
    self.key = cr.load_privatekey(cr.FILETYPE_PEM, ssl_key_pem)
237 f20cbea2 Michael Hanselmann
    del cr
238 f20cbea2 Michael Hanselmann
239 f20cbea2 Michael Hanselmann
240 b14f759e Michael Hanselmann
class _HttpSocketBase(object):
241 b14f759e Michael Hanselmann
  """Base class for HTTP server and client.
242 b14f759e Michael Hanselmann

243 b14f759e Michael Hanselmann
  """
244 b14f759e Michael Hanselmann
  def __init__(self):
245 b14f759e Michael Hanselmann
    self._using_ssl = None
246 f20cbea2 Michael Hanselmann
    self._ssl_params = None
247 b14f759e Michael Hanselmann
248 f20cbea2 Michael Hanselmann
  def _CreateSocket(self, ssl_params, ssl_verify_peer):
249 b14f759e Michael Hanselmann
    """Creates a TCP socket and initializes SSL if needed.
250 b14f759e Michael Hanselmann

251 f20cbea2 Michael Hanselmann
    @type ssl_params: HttpSslParams
252 f20cbea2 Michael Hanselmann
    @param ssl_params: SSL key and certificate
253 b14f759e Michael Hanselmann
    @type ssl_verify_peer: bool
254 b14f759e Michael Hanselmann
    @param ssl_verify_peer: Whether to require client certificate and compare
255 b14f759e Michael Hanselmann
                            it with our certificate
256 b14f759e Michael Hanselmann

257 b14f759e Michael Hanselmann
    """
258 f20cbea2 Michael Hanselmann
    self._ssl_params = ssl_params
259 f20cbea2 Michael Hanselmann
260 b14f759e Michael Hanselmann
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
261 b14f759e Michael Hanselmann
262 b14f759e Michael Hanselmann
    # Should we enable SSL?
263 f20cbea2 Michael Hanselmann
    self._using_ssl = ssl_params is not None
264 b14f759e Michael Hanselmann
265 b14f759e Michael Hanselmann
    if not self._using_ssl:
266 b14f759e Michael Hanselmann
      return sock
267 b14f759e Michael Hanselmann
268 b14f759e Michael Hanselmann
    ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
269 b14f759e Michael Hanselmann
    ctx.set_options(OpenSSL.SSL.OP_NO_SSLv2)
270 b14f759e Michael Hanselmann
271 f20cbea2 Michael Hanselmann
    ctx.use_privatekey(ssl_params.key)
272 f20cbea2 Michael Hanselmann
    ctx.use_certificate(ssl_params.cert)
273 b14f759e Michael Hanselmann
    ctx.check_privatekey()
274 b14f759e Michael Hanselmann
275 b14f759e Michael Hanselmann
    if ssl_verify_peer:
276 b14f759e Michael Hanselmann
      ctx.set_verify(OpenSSL.SSL.VERIFY_PEER |
277 b14f759e Michael Hanselmann
                     OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
278 b14f759e Michael Hanselmann
                     self._SSLVerifyCallback)
279 b14f759e Michael Hanselmann
280 b14f759e Michael Hanselmann
    return OpenSSL.SSL.Connection(ctx, sock)
281 b14f759e Michael Hanselmann
282 b14f759e Michael Hanselmann
  def _SSLVerifyCallback(self, conn, cert, errnum, errdepth, ok):
283 b14f759e Michael Hanselmann
    """Verify the certificate provided by the peer
284 b14f759e Michael Hanselmann

285 b14f759e Michael Hanselmann
    We only compare fingerprints. The client must use the same certificate as
286 b14f759e Michael Hanselmann
    we do on our side.
287 b14f759e Michael Hanselmann

288 b14f759e Michael Hanselmann
    """
289 f20cbea2 Michael Hanselmann
    assert self._ssl_params, "SSL not initialized"
290 b14f759e Michael Hanselmann
291 f20cbea2 Michael Hanselmann
    mykey = self._ssl_params.key
292 f20cbea2 Michael Hanselmann
    mycert = self._ssl_params.cert
293 f20cbea2 Michael Hanselmann
294 f20cbea2 Michael Hanselmann
    return (mycert.digest("sha1") == cert.digest("sha1") and
295 f20cbea2 Michael Hanselmann
            mycert.digest("md5") == cert.digest("md5"))
296 b14f759e Michael Hanselmann
297 b14f759e Michael Hanselmann
298 42242313 Michael Hanselmann
class _HttpConnectionHandler(object):
299 42242313 Michael Hanselmann
  """Implements server side of HTTP
300 42242313 Michael Hanselmann

301 42242313 Michael Hanselmann
  This class implements the server side of HTTP. It's based on code of Python's
302 42242313 Michael Hanselmann
  BaseHTTPServer, from both version 2.4 and 3k. It does not support non-ASCII
303 42242313 Michael Hanselmann
  character encodings. Keep-alive connections are not supported.
304 42242313 Michael Hanselmann

305 42242313 Michael Hanselmann
  """
306 42242313 Michael Hanselmann
  # The default request version.  This only affects responses up until
307 42242313 Michael Hanselmann
  # the point where the request line is parsed, so it mainly decides what
308 42242313 Michael Hanselmann
  # the client gets back when sending a malformed request line.
309 42242313 Michael Hanselmann
  # Most web servers default to HTTP 0.9, i.e. don't send a status line.
310 42242313 Michael Hanselmann
  default_request_version = HTTP_0_9
311 42242313 Michael Hanselmann
312 42242313 Michael Hanselmann
  # Error message settings
313 42242313 Michael Hanselmann
  error_message_format = DEFAULT_ERROR_MESSAGE
314 42242313 Michael Hanselmann
  error_content_type = DEFAULT_ERROR_CONTENT_TYPE
315 42242313 Michael Hanselmann
316 42242313 Michael Hanselmann
  responses = BaseHTTPServer.BaseHTTPRequestHandler.responses
317 42242313 Michael Hanselmann
318 42242313 Michael Hanselmann
  def __init__(self, server, conn, client_addr, fileio_class):
319 42242313 Michael Hanselmann
    """Initializes this class.
320 42242313 Michael Hanselmann

321 42242313 Michael Hanselmann
    Part of the initialization is reading the request and eventual POST/PUT
322 42242313 Michael Hanselmann
    data sent by the client.
323 42242313 Michael Hanselmann

324 42242313 Michael Hanselmann
    """
325 42242313 Michael Hanselmann
    self._server = server
326 42242313 Michael Hanselmann
327 42242313 Michael Hanselmann
    # We default rfile to buffered because otherwise it could be
328 42242313 Michael Hanselmann
    # really slow for large data (a getc() call per byte); we make
329 42242313 Michael Hanselmann
    # wfile unbuffered because (a) often after a write() we want to
330 42242313 Michael Hanselmann
    # read and we need to flush the line; (b) big writes to unbuffered
331 42242313 Michael Hanselmann
    # files are typically optimized by stdio even when big reads
332 42242313 Michael Hanselmann
    # aren't.
333 42242313 Michael Hanselmann
    self.rfile = fileio_class(conn, mode="rb", bufsize=-1)
334 42242313 Michael Hanselmann
    self.wfile = fileio_class(conn, mode="wb", bufsize=0)
335 42242313 Michael Hanselmann
336 42242313 Michael Hanselmann
    self.client_addr = client_addr
337 42242313 Michael Hanselmann
338 42242313 Michael Hanselmann
    self.request_headers = None
339 42242313 Michael Hanselmann
    self.request_method = None
340 42242313 Michael Hanselmann
    self.request_path = None
341 42242313 Michael Hanselmann
    self.request_requestline = None
342 42242313 Michael Hanselmann
    self.request_version = self.default_request_version
343 42242313 Michael Hanselmann
344 42242313 Michael Hanselmann
    self.response_body = None
345 42242313 Michael Hanselmann
    self.response_code = HTTP_OK
346 42242313 Michael Hanselmann
    self.response_content_type = None
347 713faea6 Oleksiy Mishchenko
    self.response_headers = {}
348 42242313 Michael Hanselmann
349 42242313 Michael Hanselmann
    self.should_fork = False
350 42242313 Michael Hanselmann
351 42242313 Michael Hanselmann
    try:
352 42242313 Michael Hanselmann
      self._ReadRequest()
353 42242313 Michael Hanselmann
      self._ReadPostData()
354 42242313 Michael Hanselmann
    except HTTPException, err:
355 42242313 Michael Hanselmann
      self._SetErrorStatus(err)
356 42242313 Michael Hanselmann
357 42242313 Michael Hanselmann
  def Close(self):
358 42242313 Michael Hanselmann
    if not self.wfile.closed:
359 42242313 Michael Hanselmann
      self.wfile.flush()
360 42242313 Michael Hanselmann
    self.wfile.close()
361 42242313 Michael Hanselmann
    self.rfile.close()
362 42242313 Michael Hanselmann
363 42242313 Michael Hanselmann
  def _DateTimeHeader(self):
364 42242313 Michael Hanselmann
    """Return the current date and time formatted for a message header.
365 42242313 Michael Hanselmann

366 42242313 Michael Hanselmann
    """
367 42242313 Michael Hanselmann
    (year, month, day, hh, mm, ss, wd, _, _) = time.gmtime()
368 42242313 Michael Hanselmann
    return ("%s, %02d %3s %4d %02d:%02d:%02d GMT" %
369 42242313 Michael Hanselmann
            (WEEKDAYNAME[wd], day, MONTHNAME[month], year, hh, mm, ss))
370 42242313 Michael Hanselmann
371 42242313 Michael Hanselmann
  def _SetErrorStatus(self, err):
372 42242313 Michael Hanselmann
    """Sets the response code and body from a HTTPException.
373 42242313 Michael Hanselmann

374 42242313 Michael Hanselmann
    @type err: HTTPException
375 42242313 Michael Hanselmann
    @param err: Exception instance
376 42242313 Michael Hanselmann

377 42242313 Michael Hanselmann
    """
378 42242313 Michael Hanselmann
    try:
379 42242313 Michael Hanselmann
      (shortmsg, longmsg) = self.responses[err.code]
380 42242313 Michael Hanselmann
    except KeyError:
381 42242313 Michael Hanselmann
      shortmsg = longmsg = "Unknown"
382 42242313 Michael Hanselmann
383 42242313 Michael Hanselmann
    if err.message:
384 42242313 Michael Hanselmann
      message = err.message
385 42242313 Michael Hanselmann
    else:
386 42242313 Michael Hanselmann
      message = shortmsg
387 42242313 Michael Hanselmann
388 42242313 Michael Hanselmann
    values = {
389 42242313 Michael Hanselmann
      "code": err.code,
390 42242313 Michael Hanselmann
      "message": cgi.escape(message),
391 42242313 Michael Hanselmann
      "explain": longmsg,
392 42242313 Michael Hanselmann
      }
393 42242313 Michael Hanselmann
394 42242313 Michael Hanselmann
    self.response_code = err.code
395 42242313 Michael Hanselmann
    self.response_content_type = self.error_content_type
396 42242313 Michael Hanselmann
    self.response_body = self.error_message_format % values
397 42242313 Michael Hanselmann
398 42242313 Michael Hanselmann
  def HandleRequest(self):
399 42242313 Michael Hanselmann
    """Handle the actual request.
400 42242313 Michael Hanselmann

401 42242313 Michael Hanselmann
    Calls the actual handler function and converts exceptions into HTTP errors.
402 42242313 Michael Hanselmann

403 42242313 Michael Hanselmann
    """
404 42242313 Michael Hanselmann
    # Don't do anything if there's already been a problem
405 42242313 Michael Hanselmann
    if self.response_code != HTTP_OK:
406 42242313 Michael Hanselmann
      return
407 42242313 Michael Hanselmann
408 42242313 Michael Hanselmann
    assert self.request_method, "Status code %s requires a method" % HTTP_OK
409 42242313 Michael Hanselmann
410 42242313 Michael Hanselmann
    # Check whether client is still there
411 42242313 Michael Hanselmann
    self.rfile.read(0)
412 42242313 Michael Hanselmann
413 42242313 Michael Hanselmann
    try:
414 42242313 Michael Hanselmann
      try:
415 42242313 Michael Hanselmann
        result = self._server.HandleRequest(self)
416 42242313 Michael Hanselmann
417 42242313 Michael Hanselmann
        # TODO: Content-type
418 42242313 Michael Hanselmann
        encoder = HTTPJsonConverter()
419 42242313 Michael Hanselmann
        body = encoder.Encode(result)
420 42242313 Michael Hanselmann
421 42242313 Michael Hanselmann
        self.response_content_type = encoder.CONTENT_TYPE
422 42242313 Michael Hanselmann
        self.response_body = body
423 42242313 Michael Hanselmann
      except (HTTPException, KeyboardInterrupt, SystemExit):
424 42242313 Michael Hanselmann
        raise
425 42242313 Michael Hanselmann
      except Exception, err:
426 42242313 Michael Hanselmann
        logging.exception("Caught exception")
427 42242313 Michael Hanselmann
        raise HTTPInternalError(message=str(err))
428 42242313 Michael Hanselmann
      except:
429 42242313 Michael Hanselmann
        logging.exception("Unknown exception")
430 42242313 Michael Hanselmann
        raise HTTPInternalError(message="Unknown error")
431 42242313 Michael Hanselmann
432 42242313 Michael Hanselmann
    except HTTPException, err:
433 42242313 Michael Hanselmann
      self._SetErrorStatus(err)
434 42242313 Michael Hanselmann
435 42242313 Michael Hanselmann
  def SendResponse(self):
436 42242313 Michael Hanselmann
    """Sends response to the client.
437 42242313 Michael Hanselmann

438 42242313 Michael Hanselmann
    """
439 42242313 Michael Hanselmann
    # Check whether client is still there
440 42242313 Michael Hanselmann
    self.rfile.read(0)
441 42242313 Michael Hanselmann
442 42242313 Michael Hanselmann
    logging.info("%s:%s %s %s", self.client_addr[0], self.client_addr[1],
443 42242313 Michael Hanselmann
                 self.request_requestline, self.response_code)
444 42242313 Michael Hanselmann
445 42242313 Michael Hanselmann
    if self.response_code in self.responses:
446 42242313 Michael Hanselmann
      response_message = self.responses[self.response_code][0]
447 42242313 Michael Hanselmann
    else:
448 42242313 Michael Hanselmann
      response_message = ""
449 42242313 Michael Hanselmann
450 42242313 Michael Hanselmann
    if self.request_version != HTTP_0_9:
451 42242313 Michael Hanselmann
      self.wfile.write("%s %d %s\r\n" %
452 42242313 Michael Hanselmann
                       (self.request_version, self.response_code,
453 42242313 Michael Hanselmann
                        response_message))
454 8a9f9060 Michael Hanselmann
      self._SendHeader(HTTP_SERVER, HTTP_GANETI_VERSION)
455 8a9f9060 Michael Hanselmann
      self._SendHeader(HTTP_DATE, self._DateTimeHeader())
456 8a9f9060 Michael Hanselmann
      self._SendHeader(HTTP_CONTENT_TYPE, self.response_content_type)
457 8a9f9060 Michael Hanselmann
      self._SendHeader(HTTP_CONTENT_LENGTH, str(len(self.response_body)))
458 713faea6 Oleksiy Mishchenko
      for key, val in self.response_headers.iteritems():
459 713faea6 Oleksiy Mishchenko
        self._SendHeader(key, val)
460 713faea6 Oleksiy Mishchenko
461 42242313 Michael Hanselmann
      # We don't support keep-alive at this time
462 8a9f9060 Michael Hanselmann
      self._SendHeader(HTTP_CONNECTION, "close")
463 42242313 Michael Hanselmann
      self.wfile.write("\r\n")
464 42242313 Michael Hanselmann
465 42242313 Michael Hanselmann
    if (self.request_method != HTTP_HEAD and
466 42242313 Michael Hanselmann
        self.response_code >= HTTP_OK and
467 42242313 Michael Hanselmann
        self.response_code not in (HTTP_NO_CONTENT, HTTP_NOT_MODIFIED)):
468 42242313 Michael Hanselmann
      self.wfile.write(self.response_body)
469 42242313 Michael Hanselmann
470 42242313 Michael Hanselmann
  def _SendHeader(self, name, value):
471 42242313 Michael Hanselmann
    if self.request_version != HTTP_0_9:
472 42242313 Michael Hanselmann
      self.wfile.write("%s: %s\r\n" % (name, value))
473 42242313 Michael Hanselmann
474 42242313 Michael Hanselmann
  def _ReadRequest(self):
475 42242313 Michael Hanselmann
    """Reads and parses request line
476 42242313 Michael Hanselmann

477 42242313 Michael Hanselmann
    """
478 42242313 Michael Hanselmann
    raw_requestline = self.rfile.readline()
479 42242313 Michael Hanselmann
480 42242313 Michael Hanselmann
    requestline = raw_requestline
481 42242313 Michael Hanselmann
    if requestline[-2:] == '\r\n':
482 42242313 Michael Hanselmann
      requestline = requestline[:-2]
483 42242313 Michael Hanselmann
    elif requestline[-1:] == '\n':
484 42242313 Michael Hanselmann
      requestline = requestline[:-1]
485 42242313 Michael Hanselmann
486 42242313 Michael Hanselmann
    if not requestline:
487 42242313 Michael Hanselmann
      raise HTTPBadRequest("Empty request line")
488 42242313 Michael Hanselmann
489 42242313 Michael Hanselmann
    self.request_requestline = requestline
490 42242313 Michael Hanselmann
491 42242313 Michael Hanselmann
    logging.debug("HTTP request: %s", raw_requestline.rstrip("\r\n"))
492 42242313 Michael Hanselmann
493 42242313 Michael Hanselmann
    words = requestline.split()
494 42242313 Michael Hanselmann
495 42242313 Michael Hanselmann
    if len(words) == 3:
496 42242313 Michael Hanselmann
      [method, path, version] = words
497 42242313 Michael Hanselmann
      if version[:5] != 'HTTP/':
498 42242313 Michael Hanselmann
        raise HTTPBadRequest("Bad request version (%r)" % version)
499 42242313 Michael Hanselmann
500 42242313 Michael Hanselmann
      try:
501 42242313 Michael Hanselmann
        base_version_number = version.split('/', 1)[1]
502 42242313 Michael Hanselmann
        version_number = base_version_number.split(".")
503 42242313 Michael Hanselmann
504 42242313 Michael Hanselmann
        # RFC 2145 section 3.1 says there can be only one "." and
505 42242313 Michael Hanselmann
        #   - major and minor numbers MUST be treated as
506 42242313 Michael Hanselmann
        #      separate integers;
507 42242313 Michael Hanselmann
        #   - HTTP/2.4 is a lower version than HTTP/2.13, which in
508 42242313 Michael Hanselmann
        #      turn is lower than HTTP/12.3;
509 42242313 Michael Hanselmann
        #   - Leading zeros MUST be ignored by recipients.
510 42242313 Michael Hanselmann
        if len(version_number) != 2:
511 42242313 Michael Hanselmann
          raise HTTPBadRequest("Bad request version (%r)" % version)
512 42242313 Michael Hanselmann
513 42242313 Michael Hanselmann
        version_number = int(version_number[0]), int(version_number[1])
514 42242313 Michael Hanselmann
      except (ValueError, IndexError):
515 42242313 Michael Hanselmann
        raise HTTPBadRequest("Bad request version (%r)" % version)
516 42242313 Michael Hanselmann
517 42242313 Michael Hanselmann
      if version_number >= (2, 0):
518 42242313 Michael Hanselmann
        raise HTTPVersionNotSupported("Invalid HTTP Version (%s)" %
519 42242313 Michael Hanselmann
                                      base_version_number)
520 42242313 Michael Hanselmann
521 42242313 Michael Hanselmann
    elif len(words) == 2:
522 42242313 Michael Hanselmann
      version = HTTP_0_9
523 42242313 Michael Hanselmann
      [method, path] = words
524 42242313 Michael Hanselmann
      if method != HTTP_GET:
525 42242313 Michael Hanselmann
        raise HTTPBadRequest("Bad HTTP/0.9 request type (%r)" % method)
526 42242313 Michael Hanselmann
527 42242313 Michael Hanselmann
    else:
528 42242313 Michael Hanselmann
      raise HTTPBadRequest("Bad request syntax (%r)" % requestline)
529 42242313 Michael Hanselmann
530 42242313 Michael Hanselmann
    # Examine the headers and look for a Connection directive
531 42242313 Michael Hanselmann
    headers = mimetools.Message(self.rfile, 0)
532 42242313 Michael Hanselmann
533 42242313 Michael Hanselmann
    self.request_method = method
534 42242313 Michael Hanselmann
    self.request_path = path
535 42242313 Michael Hanselmann
    self.request_version = version
536 42242313 Michael Hanselmann
    self.request_headers = headers
537 42242313 Michael Hanselmann
538 42242313 Michael Hanselmann
  def _ReadPostData(self):
539 42242313 Michael Hanselmann
    """Reads POST/PUT data
540 42242313 Michael Hanselmann

541 64357ed8 Michael Hanselmann
    Quoting RFC1945, section 7.2 (HTTP/1.0): "The presence of an entity body in
542 64357ed8 Michael Hanselmann
    a request is signaled by the inclusion of a Content-Length header field in
543 64357ed8 Michael Hanselmann
    the request message headers. HTTP/1.0 requests containing an entity body
544 64357ed8 Michael Hanselmann
    must include a valid Content-Length header field."
545 64357ed8 Michael Hanselmann

546 42242313 Michael Hanselmann
    """
547 64357ed8 Michael Hanselmann
    # While not according to specification, we only support an entity body for
548 64357ed8 Michael Hanselmann
    # POST and PUT.
549 8a9f9060 Michael Hanselmann
    if (not self.request_method or
550 8a9f9060 Michael Hanselmann
        self.request_method.upper() not in (HTTP_POST, HTTP_PUT)):
551 42242313 Michael Hanselmann
      self.request_post_data = None
552 42242313 Michael Hanselmann
      return
553 42242313 Michael Hanselmann
554 64357ed8 Michael Hanselmann
    content_length = None
555 42242313 Michael Hanselmann
    try:
556 64357ed8 Michael Hanselmann
      if HTTP_CONTENT_LENGTH in self.request_headers:
557 64357ed8 Michael Hanselmann
        content_length = int(self.request_headers[HTTP_CONTENT_LENGTH])
558 64357ed8 Michael Hanselmann
    except TypeError:
559 64357ed8 Michael Hanselmann
      pass
560 42242313 Michael Hanselmann
    except ValueError:
561 64357ed8 Michael Hanselmann
      pass
562 64357ed8 Michael Hanselmann
563 64357ed8 Michael Hanselmann
    # 411 Length Required is specified in RFC2616, section 10.4.12 (HTTP/1.1)
564 64357ed8 Michael Hanselmann
    if content_length is None:
565 64357ed8 Michael Hanselmann
      raise HTTPLengthRequired("Missing Content-Length header or"
566 64357ed8 Michael Hanselmann
                               " invalid format")
567 42242313 Michael Hanselmann
568 42242313 Michael Hanselmann
    data = self.rfile.read(content_length)
569 42242313 Michael Hanselmann
570 42242313 Michael Hanselmann
    # TODO: Content-type, error handling
571 7c46aafd Oleksiy Mishchenko
    if data:
572 7c46aafd Oleksiy Mishchenko
      self.request_post_data = HTTPJsonConverter().Decode(data)
573 7c46aafd Oleksiy Mishchenko
    else:
574 7c46aafd Oleksiy Mishchenko
      self.request_post_data = None
575 42242313 Michael Hanselmann
576 42242313 Michael Hanselmann
    logging.debug("HTTP POST data: %s", self.request_post_data)
577 42242313 Michael Hanselmann
578 42242313 Michael Hanselmann
579 b14f759e Michael Hanselmann
class HttpServer(_HttpSocketBase):
580 42242313 Michael Hanselmann
  """Generic HTTP server class
581 42242313 Michael Hanselmann

582 42242313 Michael Hanselmann
  Users of this class must subclass it and override the HandleRequest function.
583 42242313 Michael Hanselmann

584 42242313 Michael Hanselmann
  """
585 42242313 Michael Hanselmann
  MAX_CHILDREN = 20
586 42242313 Michael Hanselmann
587 f2a6fc9e Michael Hanselmann
  def __init__(self, mainloop, local_address, port,
588 f20cbea2 Michael Hanselmann
               ssl_params=None, ssl_verify_peer=False):
589 23e46494 Michael Hanselmann
    """Initializes the HTTP server
590 23e46494 Michael Hanselmann

591 23e46494 Michael Hanselmann
    @type mainloop: ganeti.daemon.Mainloop
592 23e46494 Michael Hanselmann
    @param mainloop: Mainloop used to poll for I/O events
593 23e46494 Michael Hanselmann
    @type local_addess: string
594 23e46494 Michael Hanselmann
    @param local_address: Local IP address to bind to
595 23e46494 Michael Hanselmann
    @type port: int
596 23e46494 Michael Hanselmann
    @param port: TCP port to listen on
597 f20cbea2 Michael Hanselmann
    @type ssl_params: HttpSslParams
598 f20cbea2 Michael Hanselmann
    @param ssl_params: SSL key and certificate
599 f2a6fc9e Michael Hanselmann
    @type ssl_verify_peer: bool
600 f2a6fc9e Michael Hanselmann
    @param ssl_verify_peer: Whether to require client certificate and compare
601 f2a6fc9e Michael Hanselmann
                            it with our certificate
602 23e46494 Michael Hanselmann

603 23e46494 Michael Hanselmann
    """
604 b14f759e Michael Hanselmann
    _HttpSocketBase.__init__(self)
605 b14f759e Michael Hanselmann
606 42242313 Michael Hanselmann
    self.mainloop = mainloop
607 23e46494 Michael Hanselmann
    self.local_address = local_address
608 23e46494 Michael Hanselmann
    self.port = port
609 42242313 Michael Hanselmann
610 f20cbea2 Michael Hanselmann
    self.socket = self._CreateSocket(ssl_params, ssl_verify_peer)
611 f2a6fc9e Michael Hanselmann
612 b14f759e Michael Hanselmann
    # Allow port to be reused
613 b14f759e Michael Hanselmann
    self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
614 42242313 Michael Hanselmann
615 b14f759e Michael Hanselmann
    if self._using_ssl:
616 42242313 Michael Hanselmann
      self._fileio_class = _SSLFileObject
617 42242313 Michael Hanselmann
    else:
618 42242313 Michael Hanselmann
      self._fileio_class = socket._fileobject
619 42242313 Michael Hanselmann
620 42242313 Michael Hanselmann
    self._children = []
621 42242313 Michael Hanselmann
622 42242313 Michael Hanselmann
    mainloop.RegisterIO(self, self.socket.fileno(), select.POLLIN)
623 42242313 Michael Hanselmann
    mainloop.RegisterSignal(self)
624 42242313 Michael Hanselmann
625 42242313 Michael Hanselmann
  def Start(self):
626 23e46494 Michael Hanselmann
    self.socket.bind((self.local_address, self.port))
627 42242313 Michael Hanselmann
    self.socket.listen(5)
628 42242313 Michael Hanselmann
629 42242313 Michael Hanselmann
  def Stop(self):
630 42242313 Michael Hanselmann
    self.socket.close()
631 42242313 Michael Hanselmann
632 42242313 Michael Hanselmann
  def OnIO(self, fd, condition):
633 42242313 Michael Hanselmann
    if condition & select.POLLIN:
634 42242313 Michael Hanselmann
      self._IncomingConnection()
635 42242313 Michael Hanselmann
636 42242313 Michael Hanselmann
  def OnSignal(self, signum):
637 42242313 Michael Hanselmann
    if signum == signal.SIGCHLD:
638 42242313 Michael Hanselmann
      self._CollectChildren(True)
639 42242313 Michael Hanselmann
640 42242313 Michael Hanselmann
  def _CollectChildren(self, quick):
641 42242313 Michael Hanselmann
    """Checks whether any child processes are done
642 42242313 Michael Hanselmann

643 42242313 Michael Hanselmann
    @type quick: bool
644 42242313 Michael Hanselmann
    @param quick: Whether to only use non-blocking functions
645 42242313 Michael Hanselmann

646 42242313 Michael Hanselmann
    """
647 42242313 Michael Hanselmann
    if not quick:
648 42242313 Michael Hanselmann
      # Don't wait for other processes if it should be a quick check
649 42242313 Michael Hanselmann
      while len(self._children) > self.MAX_CHILDREN:
650 42242313 Michael Hanselmann
        try:
651 6526ddcd Michael Hanselmann
          # Waiting without a timeout brings us into a potential DoS situation.
652 6526ddcd Michael Hanselmann
          # As soon as too many children run, we'll not respond to new
653 6526ddcd Michael Hanselmann
          # requests. The real solution would be to add a timeout for children
654 6526ddcd Michael Hanselmann
          # and killing them after some time.
655 42242313 Michael Hanselmann
          pid, status = os.waitpid(0, 0)
656 42242313 Michael Hanselmann
        except os.error:
657 42242313 Michael Hanselmann
          pid = None
658 42242313 Michael Hanselmann
        if pid and pid in self._children:
659 42242313 Michael Hanselmann
          self._children.remove(pid)
660 42242313 Michael Hanselmann
661 42242313 Michael Hanselmann
    for child in self._children:
662 42242313 Michael Hanselmann
      try:
663 42242313 Michael Hanselmann
        pid, status = os.waitpid(child, os.WNOHANG)
664 42242313 Michael Hanselmann
      except os.error:
665 42242313 Michael Hanselmann
        pid = None
666 42242313 Michael Hanselmann
      if pid and pid in self._children:
667 42242313 Michael Hanselmann
        self._children.remove(pid)
668 42242313 Michael Hanselmann
669 42242313 Michael Hanselmann
  def _IncomingConnection(self):
670 6526ddcd Michael Hanselmann
    """Called for each incoming connection
671 42242313 Michael Hanselmann

672 6526ddcd Michael Hanselmann
    """
673 6526ddcd Michael Hanselmann
    (connection, client_addr) = self.socket.accept()
674 42242313 Michael Hanselmann
675 42242313 Michael Hanselmann
    self._CollectChildren(False)
676 42242313 Michael Hanselmann
677 42242313 Michael Hanselmann
    pid = os.fork()
678 42242313 Michael Hanselmann
    if pid == 0:
679 42242313 Michael Hanselmann
      # Child process
680 6526ddcd Michael Hanselmann
      logging.info("Connection from %s:%s", client_addr[0], client_addr[1])
681 6526ddcd Michael Hanselmann
682 42242313 Michael Hanselmann
      try:
683 6526ddcd Michael Hanselmann
        try:
684 6526ddcd Michael Hanselmann
          try:
685 6526ddcd Michael Hanselmann
            handler = None
686 6526ddcd Michael Hanselmann
            try:
687 6526ddcd Michael Hanselmann
              # Read, parse and handle request
688 6526ddcd Michael Hanselmann
              handler = _HttpConnectionHandler(self, connection, client_addr,
689 6526ddcd Michael Hanselmann
                                               self._fileio_class)
690 6526ddcd Michael Hanselmann
              handler.HandleRequest()
691 6526ddcd Michael Hanselmann
            finally:
692 6526ddcd Michael Hanselmann
              # Try to send a response
693 6526ddcd Michael Hanselmann
              if handler:
694 6526ddcd Michael Hanselmann
                handler.SendResponse()
695 6526ddcd Michael Hanselmann
                handler.Close()
696 6526ddcd Michael Hanselmann
          except SocketClosed:
697 6526ddcd Michael Hanselmann
            pass
698 6526ddcd Michael Hanselmann
        finally:
699 6526ddcd Michael Hanselmann
          logging.info("Disconnected %s:%s", client_addr[0], client_addr[1])
700 42242313 Michael Hanselmann
      except:
701 42242313 Michael Hanselmann
        logging.exception("Error while handling request from %s:%s",
702 42242313 Michael Hanselmann
                          client_addr[0], client_addr[1])
703 42242313 Michael Hanselmann
        os._exit(1)
704 42242313 Michael Hanselmann
      os._exit(0)
705 42242313 Michael Hanselmann
    else:
706 42242313 Michael Hanselmann
      self._children.append(pid)
707 42242313 Michael Hanselmann
708 42242313 Michael Hanselmann
  def HandleRequest(self, req):
709 42242313 Michael Hanselmann
    raise NotImplementedError()
710 42242313 Michael Hanselmann
711 42242313 Michael Hanselmann
712 8a0b06d2 Michael Hanselmann
class HttpClientRequest(object):
713 438a366a Michael Hanselmann
  def __init__(self, host, port, method, path, headers=None, post_data=None,
714 f20cbea2 Michael Hanselmann
               ssl_params=None, ssl_verify_peer=False):
715 8a0b06d2 Michael Hanselmann
    """Describes an HTTP request.
716 8a0b06d2 Michael Hanselmann

717 8a0b06d2 Michael Hanselmann
    @type host: string
718 8a0b06d2 Michael Hanselmann
    @param host: Hostname
719 8a0b06d2 Michael Hanselmann
    @type port: int
720 8a0b06d2 Michael Hanselmann
    @param port: Port
721 8a0b06d2 Michael Hanselmann
    @type method: string
722 8a0b06d2 Michael Hanselmann
    @param method: Method name
723 8a0b06d2 Michael Hanselmann
    @type path: string
724 8a0b06d2 Michael Hanselmann
    @param path: Request path
725 8a0b06d2 Michael Hanselmann
    @type headers: dict or None
726 8a0b06d2 Michael Hanselmann
    @param headers: Additional headers to send
727 8a0b06d2 Michael Hanselmann
    @type post_data: string or None
728 8a0b06d2 Michael Hanselmann
    @param post_data: Additional data to send
729 f20cbea2 Michael Hanselmann
    @type ssl_params: HttpSslParams
730 f20cbea2 Michael Hanselmann
    @param ssl_params: SSL key and certificate
731 f20cbea2 Michael Hanselmann
    @type ssl_verify_peer: bool
732 f20cbea2 Michael Hanselmann
    @param ssl_verify_peer: Whether to compare our certificate with server's
733 f20cbea2 Michael Hanselmann
                            certificate
734 8a0b06d2 Michael Hanselmann

735 8a0b06d2 Michael Hanselmann
    """
736 8a0b06d2 Michael Hanselmann
    if post_data is not None:
737 8a0b06d2 Michael Hanselmann
      assert method.upper() in (HTTP_POST, HTTP_PUT), \
738 8a0b06d2 Michael Hanselmann
        "Only POST and GET requests support sending data"
739 8a0b06d2 Michael Hanselmann
740 8a0b06d2 Michael Hanselmann
    assert path.startswith("/"), "Path must start with slash (/)"
741 8a0b06d2 Michael Hanselmann
742 8a0b06d2 Michael Hanselmann
    self.host = host
743 8a0b06d2 Michael Hanselmann
    self.port = port
744 f20cbea2 Michael Hanselmann
    self.ssl_params = ssl_params
745 438a366a Michael Hanselmann
    self.ssl_verify_peer = ssl_verify_peer
746 8a0b06d2 Michael Hanselmann
    self.method = method
747 8a0b06d2 Michael Hanselmann
    self.path = path
748 8a0b06d2 Michael Hanselmann
    self.headers = headers
749 8a0b06d2 Michael Hanselmann
    self.post_data = post_data
750 8a0b06d2 Michael Hanselmann
751 8a0b06d2 Michael Hanselmann
    self.success = None
752 8a0b06d2 Michael Hanselmann
    self.error = None
753 8a0b06d2 Michael Hanselmann
754 8a0b06d2 Michael Hanselmann
    self.resp_status_line = None
755 8a0b06d2 Michael Hanselmann
    self.resp_version = None
756 8a0b06d2 Michael Hanselmann
    self.resp_status = None
757 8a0b06d2 Michael Hanselmann
    self.resp_reason = None
758 8a0b06d2 Michael Hanselmann
    self.resp_headers = None
759 8a0b06d2 Michael Hanselmann
    self.resp_body = None
760 8a0b06d2 Michael Hanselmann
761 8a0b06d2 Michael Hanselmann
762 438a366a Michael Hanselmann
class HttpClientRequestExecutor(_HttpSocketBase):
763 8a0b06d2 Michael Hanselmann
  # Default headers
764 8a0b06d2 Michael Hanselmann
  DEFAULT_HEADERS = {
765 8a0b06d2 Michael Hanselmann
    HTTP_USER_AGENT: HTTP_GANETI_VERSION,
766 8a0b06d2 Michael Hanselmann
    # TODO: For keep-alive, don't send "Connection: close"
767 8a0b06d2 Michael Hanselmann
    HTTP_CONNECTION: "close",
768 8a0b06d2 Michael Hanselmann
    }
769 8a0b06d2 Michael Hanselmann
770 8a0b06d2 Michael Hanselmann
  # Length limits
771 8a0b06d2 Michael Hanselmann
  STATUS_LINE_LENGTH_MAX = 512
772 8a0b06d2 Michael Hanselmann
  HEADER_LENGTH_MAX = 4 * 1024
773 8a0b06d2 Michael Hanselmann
774 438a366a Michael Hanselmann
  # Timeouts in seconds for socket layer
775 8a0b06d2 Michael Hanselmann
  # TODO: Make read timeout configurable per OpCode
776 8a0b06d2 Michael Hanselmann
  CONNECT_TIMEOUT = 5.0
777 8a0b06d2 Michael Hanselmann
  WRITE_TIMEOUT = 10
778 8a0b06d2 Michael Hanselmann
  READ_TIMEOUT = None
779 8a0b06d2 Michael Hanselmann
  CLOSE_TIMEOUT = 1
780 8a0b06d2 Michael Hanselmann
781 8a0b06d2 Michael Hanselmann
  # Parser state machine
782 8a0b06d2 Michael Hanselmann
  PS_STATUS_LINE = "status-line"
783 8a0b06d2 Michael Hanselmann
  PS_HEADERS = "headers"
784 8a0b06d2 Michael Hanselmann
  PS_BODY = "body"
785 8a0b06d2 Michael Hanselmann
  PS_COMPLETE = "complete"
786 8a0b06d2 Michael Hanselmann
787 438a366a Michael Hanselmann
  # Socket operations
788 438a366a Michael Hanselmann
  (OP_SEND,
789 438a366a Michael Hanselmann
   OP_RECV,
790 438a366a Michael Hanselmann
   OP_CLOSE_CHECK,
791 438a366a Michael Hanselmann
   OP_SHUTDOWN) = range(4)
792 438a366a Michael Hanselmann
793 8a0b06d2 Michael Hanselmann
  def __init__(self, req):
794 8a0b06d2 Michael Hanselmann
    """Initializes the HttpClientRequestExecutor class.
795 8a0b06d2 Michael Hanselmann

796 8a0b06d2 Michael Hanselmann
    @type req: HttpClientRequest
797 8a0b06d2 Michael Hanselmann
    @param req: Request object
798 8a0b06d2 Michael Hanselmann

799 8a0b06d2 Michael Hanselmann
    """
800 438a366a Michael Hanselmann
    _HttpSocketBase.__init__(self)
801 438a366a Michael Hanselmann
802 8a0b06d2 Michael Hanselmann
    self.request = req
803 8a0b06d2 Michael Hanselmann
804 8a0b06d2 Michael Hanselmann
    self.parser_status = self.PS_STATUS_LINE
805 8a0b06d2 Michael Hanselmann
    self.header_buffer = StringIO()
806 8a0b06d2 Michael Hanselmann
    self.body_buffer = StringIO()
807 8a0b06d2 Michael Hanselmann
    self.content_length = None
808 8a0b06d2 Michael Hanselmann
    self.server_will_close = None
809 8a0b06d2 Michael Hanselmann
810 8a0b06d2 Michael Hanselmann
    self.poller = select.poll()
811 8a0b06d2 Michael Hanselmann
812 8a0b06d2 Michael Hanselmann
    try:
813 8a0b06d2 Michael Hanselmann
      # TODO: Implement connection caching/keep-alive
814 f20cbea2 Michael Hanselmann
      self.sock = self._CreateSocket(req.ssl_params,
815 438a366a Michael Hanselmann
                                     req.ssl_verify_peer)
816 8a0b06d2 Michael Hanselmann
817 8a0b06d2 Michael Hanselmann
      # Disable Python's timeout
818 8a0b06d2 Michael Hanselmann
      self.sock.settimeout(None)
819 8a0b06d2 Michael Hanselmann
820 8a0b06d2 Michael Hanselmann
      # Operate in non-blocking mode
821 8a0b06d2 Michael Hanselmann
      self.sock.setblocking(0)
822 8a0b06d2 Michael Hanselmann
823 8a0b06d2 Michael Hanselmann
      force_close = True
824 8a0b06d2 Michael Hanselmann
      self._Connect()
825 8a0b06d2 Michael Hanselmann
      try:
826 8a0b06d2 Michael Hanselmann
        self._SendRequest()
827 8a0b06d2 Michael Hanselmann
        self._ReadResponse()
828 8a0b06d2 Michael Hanselmann
829 8a0b06d2 Michael Hanselmann
        # Only wait for server to close if we didn't have any exception.
830 8a0b06d2 Michael Hanselmann
        force_close = False
831 8a0b06d2 Michael Hanselmann
      finally:
832 8a0b06d2 Michael Hanselmann
        self._CloseConnection(force_close)
833 8a0b06d2 Michael Hanselmann
834 8a0b06d2 Michael Hanselmann
      self.sock.close()
835 8a0b06d2 Michael Hanselmann
      self.sock = None
836 8a0b06d2 Michael Hanselmann
837 8a0b06d2 Michael Hanselmann
      req.resp_body = self.body_buffer.getvalue()
838 8a0b06d2 Michael Hanselmann
839 8a0b06d2 Michael Hanselmann
      req.success = True
840 8a0b06d2 Michael Hanselmann
      req.error = None
841 8a0b06d2 Michael Hanselmann
842 438a366a Michael Hanselmann
    except _HttpClientError, err:
843 8a0b06d2 Michael Hanselmann
      req.success = False
844 8a0b06d2 Michael Hanselmann
      req.error = str(err)
845 8a0b06d2 Michael Hanselmann
846 8a0b06d2 Michael Hanselmann
  def _BuildRequest(self):
847 8a0b06d2 Michael Hanselmann
    """Build HTTP request.
848 8a0b06d2 Michael Hanselmann

849 8a0b06d2 Michael Hanselmann
    @rtype: string
850 8a0b06d2 Michael Hanselmann
    @return: Complete request
851 8a0b06d2 Michael Hanselmann

852 8a0b06d2 Michael Hanselmann
    """
853 8a0b06d2 Michael Hanselmann
    # Headers
854 8a0b06d2 Michael Hanselmann
    send_headers = self.DEFAULT_HEADERS.copy()
855 8a0b06d2 Michael Hanselmann
856 8a0b06d2 Michael Hanselmann
    if self.request.headers:
857 33bbdbec Michael Hanselmann
      send_headers.update(self.request.headers)
858 8a0b06d2 Michael Hanselmann
859 8a0b06d2 Michael Hanselmann
    send_headers[HTTP_HOST] = "%s:%s" % (self.request.host, self.request.port)
860 8a0b06d2 Michael Hanselmann
861 8a0b06d2 Michael Hanselmann
    if self.request.post_data:
862 8a0b06d2 Michael Hanselmann
      send_headers[HTTP_CONTENT_LENGTH] = len(self.request.post_data)
863 8a0b06d2 Michael Hanselmann
864 8a0b06d2 Michael Hanselmann
    buf = StringIO()
865 8a0b06d2 Michael Hanselmann
866 8a0b06d2 Michael Hanselmann
    # Add request line. We only support HTTP/1.0 (no chunked transfers and no
867 8a0b06d2 Michael Hanselmann
    # keep-alive).
868 8a0b06d2 Michael Hanselmann
    # TODO: For keep-alive, change to HTTP/1.1
869 8a0b06d2 Michael Hanselmann
    buf.write("%s %s %s\r\n" % (self.request.method.upper(),
870 8a0b06d2 Michael Hanselmann
                                self.request.path, HTTP_1_0))
871 8a0b06d2 Michael Hanselmann
872 8a0b06d2 Michael Hanselmann
    # Add headers
873 8a0b06d2 Michael Hanselmann
    for name, value in send_headers.iteritems():
874 8a0b06d2 Michael Hanselmann
      buf.write("%s: %s\r\n" % (name, value))
875 8a0b06d2 Michael Hanselmann
876 8a0b06d2 Michael Hanselmann
    buf.write("\r\n")
877 8a0b06d2 Michael Hanselmann
878 8a0b06d2 Michael Hanselmann
    if self.request.post_data:
879 8a0b06d2 Michael Hanselmann
      buf.write(self.request.post_data)
880 8a0b06d2 Michael Hanselmann
881 8a0b06d2 Michael Hanselmann
    return buf.getvalue()
882 8a0b06d2 Michael Hanselmann
883 8a0b06d2 Michael Hanselmann
  def _ParseStatusLine(self):
884 8a0b06d2 Michael Hanselmann
    """Parses the status line sent by the server.
885 8a0b06d2 Michael Hanselmann

886 8a0b06d2 Michael Hanselmann
    """
887 8a0b06d2 Michael Hanselmann
    line = self.request.resp_status_line
888 8a0b06d2 Michael Hanselmann
889 8a0b06d2 Michael Hanselmann
    if not line:
890 438a366a Michael Hanselmann
      raise _HttpClientError("Empty status line")
891 8a0b06d2 Michael Hanselmann
892 8a0b06d2 Michael Hanselmann
    try:
893 8a0b06d2 Michael Hanselmann
      [version, status, reason] = line.split(None, 2)
894 8a0b06d2 Michael Hanselmann
    except ValueError:
895 8a0b06d2 Michael Hanselmann
      try:
896 8a0b06d2 Michael Hanselmann
        [version, status] = line.split(None, 1)
897 8a0b06d2 Michael Hanselmann
        reason = ""
898 8a0b06d2 Michael Hanselmann
      except ValueError:
899 8a0b06d2 Michael Hanselmann
        version = HTTP_9_0
900 8a0b06d2 Michael Hanselmann
901 8a0b06d2 Michael Hanselmann
    if version:
902 8a0b06d2 Michael Hanselmann
      version = version.upper()
903 8a0b06d2 Michael Hanselmann
904 8a0b06d2 Michael Hanselmann
    if version not in (HTTP_1_0, HTTP_1_1):
905 8a0b06d2 Michael Hanselmann
      # We do not support HTTP/0.9, despite the specification requiring it
906 8a0b06d2 Michael Hanselmann
      # (RFC2616, section 19.6)
907 438a366a Michael Hanselmann
      raise _HttpClientError("Only HTTP/1.0 and HTTP/1.1 are supported (%r)" %
908 438a366a Michael Hanselmann
                             line)
909 8a0b06d2 Michael Hanselmann
910 8a0b06d2 Michael Hanselmann
    # The status code is a three-digit number
911 8a0b06d2 Michael Hanselmann
    try:
912 8a0b06d2 Michael Hanselmann
      status = int(status)
913 8a0b06d2 Michael Hanselmann
      if status < 100 or status > 999:
914 8a0b06d2 Michael Hanselmann
        status = -1
915 8a0b06d2 Michael Hanselmann
    except ValueError:
916 8a0b06d2 Michael Hanselmann
      status = -1
917 8a0b06d2 Michael Hanselmann
918 8a0b06d2 Michael Hanselmann
    if status == -1:
919 438a366a Michael Hanselmann
      raise _HttpClientError("Invalid status code (%r)" % line)
920 8a0b06d2 Michael Hanselmann
921 8a0b06d2 Michael Hanselmann
    self.request.resp_version = version
922 8a0b06d2 Michael Hanselmann
    self.request.resp_status = status
923 8a0b06d2 Michael Hanselmann
    self.request.resp_reason = reason
924 8a0b06d2 Michael Hanselmann
925 8a0b06d2 Michael Hanselmann
  def _WillServerCloseConnection(self):
926 8a0b06d2 Michael Hanselmann
    """Evaluate whether server will close the connection.
927 8a0b06d2 Michael Hanselmann

928 8a0b06d2 Michael Hanselmann
    @rtype: bool
929 8a0b06d2 Michael Hanselmann
    @return: Whether server will close the connection
930 8a0b06d2 Michael Hanselmann

931 8a0b06d2 Michael Hanselmann
    """
932 8a0b06d2 Michael Hanselmann
    hdr_connection = self.request.resp_headers.get(HTTP_CONNECTION, None)
933 8a0b06d2 Michael Hanselmann
    if hdr_connection:
934 8a0b06d2 Michael Hanselmann
      hdr_connection = hdr_connection.lower()
935 8a0b06d2 Michael Hanselmann
936 8a0b06d2 Michael Hanselmann
    # An HTTP/1.1 server is assumed to stay open unless explicitly closed.
937 8a0b06d2 Michael Hanselmann
    if self.request.resp_version == HTTP_1_1:
938 8a0b06d2 Michael Hanselmann
      return (hdr_connection and "close" in hdr_connection)
939 8a0b06d2 Michael Hanselmann
940 8a0b06d2 Michael Hanselmann
    # Some HTTP/1.0 implementations have support for persistent connections,
941 8a0b06d2 Michael Hanselmann
    # using rules different than HTTP/1.1.
942 8a0b06d2 Michael Hanselmann
943 8a0b06d2 Michael Hanselmann
    # For older HTTP, Keep-Alive indicates persistent connection.
944 8a0b06d2 Michael Hanselmann
    if self.request.resp_headers.get(HTTP_KEEP_ALIVE):
945 8a0b06d2 Michael Hanselmann
      return False
946 8a0b06d2 Michael Hanselmann
947 8a0b06d2 Michael Hanselmann
    # At least Akamai returns a "Connection: Keep-Alive" header, which was
948 8a0b06d2 Michael Hanselmann
    # supposed to be sent by the client.
949 8a0b06d2 Michael Hanselmann
    if hdr_connection and "keep-alive" in hdr_connection:
950 8a0b06d2 Michael Hanselmann
      return False
951 8a0b06d2 Michael Hanselmann
952 8a0b06d2 Michael Hanselmann
    return True
953 8a0b06d2 Michael Hanselmann
954 8a0b06d2 Michael Hanselmann
  def _ParseHeaders(self):
955 8a0b06d2 Michael Hanselmann
    """Parses the headers sent by the server.
956 8a0b06d2 Michael Hanselmann

957 8a0b06d2 Michael Hanselmann
    This function also adjusts internal variables based on the header values.
958 8a0b06d2 Michael Hanselmann

959 8a0b06d2 Michael Hanselmann
    """
960 8a0b06d2 Michael Hanselmann
    req = self.request
961 8a0b06d2 Michael Hanselmann
962 8a0b06d2 Michael Hanselmann
    # Parse headers
963 8a0b06d2 Michael Hanselmann
    self.header_buffer.seek(0, 0)
964 8a0b06d2 Michael Hanselmann
    req.resp_headers = mimetools.Message(self.header_buffer, 0)
965 8a0b06d2 Michael Hanselmann
966 8a0b06d2 Michael Hanselmann
    self.server_will_close = self._WillServerCloseConnection()
967 8a0b06d2 Michael Hanselmann
968 8a0b06d2 Michael Hanselmann
    # Do we have a Content-Length header?
969 8a0b06d2 Michael Hanselmann
    hdr_content_length = req.resp_headers.get(HTTP_CONTENT_LENGTH, None)
970 8a0b06d2 Michael Hanselmann
    if hdr_content_length:
971 8a0b06d2 Michael Hanselmann
      try:
972 8a0b06d2 Michael Hanselmann
        self.content_length = int(hdr_content_length)
973 8a0b06d2 Michael Hanselmann
      except ValueError:
974 8a0b06d2 Michael Hanselmann
        pass
975 8a0b06d2 Michael Hanselmann
      if self.content_length is not None and self.content_length < 0:
976 8a0b06d2 Michael Hanselmann
        self.content_length = None
977 8a0b06d2 Michael Hanselmann
978 8a0b06d2 Michael Hanselmann
    # does the body have a fixed length? (of zero)
979 8a0b06d2 Michael Hanselmann
    if (req.resp_status in (HTTP_NO_CONTENT, HTTP_NOT_MODIFIED) or
980 8a0b06d2 Michael Hanselmann
        100 <= req.resp_status < 200 or req.method == HTTP_HEAD):
981 8a0b06d2 Michael Hanselmann
      self.content_length = 0
982 8a0b06d2 Michael Hanselmann
983 8a0b06d2 Michael Hanselmann
    # if the connection remains open and a content-length was not provided,
984 8a0b06d2 Michael Hanselmann
    # then assume that the connection WILL close.
985 8a0b06d2 Michael Hanselmann
    if self.content_length is None:
986 8a0b06d2 Michael Hanselmann
      self.server_will_close = True
987 8a0b06d2 Michael Hanselmann
988 8a0b06d2 Michael Hanselmann
  def _CheckStatusLineLength(self, length):
989 8a0b06d2 Michael Hanselmann
    if length > self.STATUS_LINE_LENGTH_MAX:
990 438a366a Michael Hanselmann
      raise _HttpClientError("Status line longer than %d chars" %
991 438a366a Michael Hanselmann
                             self.STATUS_LINE_LENGTH_MAX)
992 8a0b06d2 Michael Hanselmann
993 8a0b06d2 Michael Hanselmann
  def _CheckHeaderLength(self, length):
994 8a0b06d2 Michael Hanselmann
    if length > self.HEADER_LENGTH_MAX:
995 438a366a Michael Hanselmann
      raise _HttpClientError("Headers longer than %d chars" %
996 438a366a Michael Hanselmann
                             self.HEADER_LENGTH_MAX)
997 8a0b06d2 Michael Hanselmann
998 8a0b06d2 Michael Hanselmann
  def _ParseBuffer(self, buf, eof):
999 8a0b06d2 Michael Hanselmann
    """Main function for HTTP response state machine.
1000 8a0b06d2 Michael Hanselmann

1001 8a0b06d2 Michael Hanselmann
    @type buf: string
1002 8a0b06d2 Michael Hanselmann
    @param buf: Receive buffer
1003 8a0b06d2 Michael Hanselmann
    @type eof: bool
1004 8a0b06d2 Michael Hanselmann
    @param eof: Whether we've reached EOF on the socket
1005 8a0b06d2 Michael Hanselmann
    @rtype: string
1006 8a0b06d2 Michael Hanselmann
    @return: Updated receive buffer
1007 8a0b06d2 Michael Hanselmann

1008 8a0b06d2 Michael Hanselmann
    """
1009 8a0b06d2 Michael Hanselmann
    if self.parser_status == self.PS_STATUS_LINE:
1010 8a0b06d2 Michael Hanselmann
      # Expect status line
1011 8a0b06d2 Michael Hanselmann
      idx = buf.find("\r\n")
1012 8a0b06d2 Michael Hanselmann
      if idx >= 0:
1013 8a0b06d2 Michael Hanselmann
        self.request.resp_status_line = buf[:idx]
1014 8a0b06d2 Michael Hanselmann
1015 8a0b06d2 Michael Hanselmann
        self._CheckStatusLineLength(len(self.request.resp_status_line))
1016 8a0b06d2 Michael Hanselmann
1017 8a0b06d2 Michael Hanselmann
        # Remove status line, including CRLF
1018 8a0b06d2 Michael Hanselmann
        buf = buf[idx + 2:]
1019 8a0b06d2 Michael Hanselmann
1020 8a0b06d2 Michael Hanselmann
        self._ParseStatusLine()
1021 8a0b06d2 Michael Hanselmann
1022 8a0b06d2 Michael Hanselmann
        self.parser_status = self.PS_HEADERS
1023 8a0b06d2 Michael Hanselmann
      else:
1024 8a0b06d2 Michael Hanselmann
        # Check whether incoming data is getting too large, otherwise we just
1025 8a0b06d2 Michael Hanselmann
        # fill our read buffer.
1026 8a0b06d2 Michael Hanselmann
        self._CheckStatusLineLength(len(buf))
1027 8a0b06d2 Michael Hanselmann
1028 8a0b06d2 Michael Hanselmann
    if self.parser_status == self.PS_HEADERS:
1029 8a0b06d2 Michael Hanselmann
      # Wait for header end
1030 8a0b06d2 Michael Hanselmann
      idx = buf.find("\r\n\r\n")
1031 8a0b06d2 Michael Hanselmann
      if idx >= 0:
1032 8a0b06d2 Michael Hanselmann
        self.header_buffer.write(buf[:idx + 2])
1033 8a0b06d2 Michael Hanselmann
1034 8a0b06d2 Michael Hanselmann
        self._CheckHeaderLength(self.header_buffer.tell())
1035 8a0b06d2 Michael Hanselmann
1036 8a0b06d2 Michael Hanselmann
        # Remove headers, including CRLF
1037 8a0b06d2 Michael Hanselmann
        buf = buf[idx + 4:]
1038 8a0b06d2 Michael Hanselmann
1039 8a0b06d2 Michael Hanselmann
        self._ParseHeaders()
1040 8a0b06d2 Michael Hanselmann
1041 8a0b06d2 Michael Hanselmann
        self.parser_status = self.PS_BODY
1042 8a0b06d2 Michael Hanselmann
      else:
1043 8a0b06d2 Michael Hanselmann
        # Check whether incoming data is getting too large, otherwise we just
1044 8a0b06d2 Michael Hanselmann
        # fill our read buffer.
1045 8a0b06d2 Michael Hanselmann
        self._CheckHeaderLength(len(buf))
1046 8a0b06d2 Michael Hanselmann
1047 8a0b06d2 Michael Hanselmann
    if self.parser_status == self.PS_BODY:
1048 8a0b06d2 Michael Hanselmann
      self.body_buffer.write(buf)
1049 8a0b06d2 Michael Hanselmann
      buf = ""
1050 8a0b06d2 Michael Hanselmann
1051 8a0b06d2 Michael Hanselmann
      # Check whether we've read everything
1052 8a0b06d2 Michael Hanselmann
      if (eof or
1053 8a0b06d2 Michael Hanselmann
          (self.content_length is not None and
1054 8a0b06d2 Michael Hanselmann
           self.body_buffer.tell() >= self.content_length)):
1055 8a0b06d2 Michael Hanselmann
        self.parser_status = self.PS_COMPLETE
1056 8a0b06d2 Michael Hanselmann
1057 8a0b06d2 Michael Hanselmann
    return buf
1058 8a0b06d2 Michael Hanselmann
1059 8a0b06d2 Michael Hanselmann
  def _WaitForCondition(self, event, timeout):
1060 8a0b06d2 Michael Hanselmann
    """Waits for a condition to occur on the socket.
1061 8a0b06d2 Michael Hanselmann

1062 8a0b06d2 Michael Hanselmann
    @type event: int
1063 8a0b06d2 Michael Hanselmann
    @param event: ORed condition (see select module)
1064 8a0b06d2 Michael Hanselmann
    @type timeout: float or None
1065 8a0b06d2 Michael Hanselmann
    @param timeout: Timeout in seconds
1066 8a0b06d2 Michael Hanselmann
    @rtype: int or None
1067 8a0b06d2 Michael Hanselmann
    @return: None for timeout, otherwise occured conditions
1068 8a0b06d2 Michael Hanselmann

1069 8a0b06d2 Michael Hanselmann
    """
1070 8a0b06d2 Michael Hanselmann
    check = (event | select.POLLPRI |
1071 8a0b06d2 Michael Hanselmann
             select.POLLNVAL | select.POLLHUP | select.POLLERR)
1072 8a0b06d2 Michael Hanselmann
1073 8a0b06d2 Michael Hanselmann
    if timeout is not None:
1074 8a0b06d2 Michael Hanselmann
      # Poller object expects milliseconds
1075 8a0b06d2 Michael Hanselmann
      timeout *= 1000
1076 8a0b06d2 Michael Hanselmann
1077 8a0b06d2 Michael Hanselmann
    self.poller.register(self.sock, event)
1078 8a0b06d2 Michael Hanselmann
    try:
1079 8a0b06d2 Michael Hanselmann
      while True:
1080 8a0b06d2 Michael Hanselmann
        # TODO: If the main thread receives a signal and we have no timeout, we
1081 8a0b06d2 Michael Hanselmann
        # could wait forever. This should check a global "quit" flag or
1082 8a0b06d2 Michael Hanselmann
        # something every so often.
1083 8a0b06d2 Michael Hanselmann
        io_events = self.poller.poll(timeout)
1084 8a0b06d2 Michael Hanselmann
        if io_events:
1085 8a0b06d2 Michael Hanselmann
          for (evfd, evcond) in io_events:
1086 8a0b06d2 Michael Hanselmann
            if evcond & check:
1087 8a0b06d2 Michael Hanselmann
              return evcond
1088 8a0b06d2 Michael Hanselmann
        else:
1089 8a0b06d2 Michael Hanselmann
          # Timeout
1090 8a0b06d2 Michael Hanselmann
          return None
1091 8a0b06d2 Michael Hanselmann
    finally:
1092 8a0b06d2 Michael Hanselmann
      self.poller.unregister(self.sock)
1093 8a0b06d2 Michael Hanselmann
1094 438a366a Michael Hanselmann
  def _SocketOperation(self, op, arg1, error_msg, timeout_msg):
1095 438a366a Michael Hanselmann
    """Wrapper around socket functions.
1096 438a366a Michael Hanselmann

1097 438a366a Michael Hanselmann
    This function abstracts error handling for socket operations, especially
1098 438a366a Michael Hanselmann
    for the complicated interaction with OpenSSL.
1099 438a366a Michael Hanselmann

1100 438a366a Michael Hanselmann
    """
1101 438a366a Michael Hanselmann
    if op == self.OP_SEND:
1102 438a366a Michael Hanselmann
      event_poll = select.POLLOUT
1103 438a366a Michael Hanselmann
      event_check = select.POLLOUT
1104 438a366a Michael Hanselmann
      timeout = self.WRITE_TIMEOUT
1105 438a366a Michael Hanselmann
1106 438a366a Michael Hanselmann
    elif op in (self.OP_RECV, self.OP_CLOSE_CHECK):
1107 438a366a Michael Hanselmann
      event_poll = select.POLLIN
1108 438a366a Michael Hanselmann
      event_check = select.POLLIN | select.POLLPRI
1109 438a366a Michael Hanselmann
      if op == self.OP_CLOSE_CHECK:
1110 438a366a Michael Hanselmann
        timeout = self.CLOSE_TIMEOUT
1111 438a366a Michael Hanselmann
      else:
1112 438a366a Michael Hanselmann
        timeout = self.READ_TIMEOUT
1113 438a366a Michael Hanselmann
1114 438a366a Michael Hanselmann
    elif op == self.OP_SHUTDOWN:
1115 438a366a Michael Hanselmann
      event_poll = None
1116 438a366a Michael Hanselmann
      event_check = None
1117 438a366a Michael Hanselmann
1118 438a366a Michael Hanselmann
      # The timeout is only used when OpenSSL requests polling for a condition.
1119 438a366a Michael Hanselmann
      # It is not advisable to have no timeout for shutdown.
1120 438a366a Michael Hanselmann
      timeout = self.WRITE_TIMEOUT
1121 438a366a Michael Hanselmann
1122 438a366a Michael Hanselmann
    else:
1123 438a366a Michael Hanselmann
      raise AssertionError("Invalid socket operation")
1124 438a366a Michael Hanselmann
1125 438a366a Michael Hanselmann
    # No override by default
1126 438a366a Michael Hanselmann
    event_override = 0
1127 438a366a Michael Hanselmann
1128 438a366a Michael Hanselmann
    while True:
1129 438a366a Michael Hanselmann
      # Poll only for certain operations and when asked for by an override
1130 438a366a Michael Hanselmann
      if (event_override or
1131 438a366a Michael Hanselmann
          op in (self.OP_SEND, self.OP_RECV, self.OP_CLOSE_CHECK)):
1132 438a366a Michael Hanselmann
        if event_override:
1133 438a366a Michael Hanselmann
          wait_for_event = event_override
1134 438a366a Michael Hanselmann
        else:
1135 438a366a Michael Hanselmann
          wait_for_event = event_poll
1136 438a366a Michael Hanselmann
1137 438a366a Michael Hanselmann
        event = self._WaitForCondition(wait_for_event, timeout)
1138 438a366a Michael Hanselmann
        if event is None:
1139 438a366a Michael Hanselmann
          raise _HttpClientTimeout(timeout_msg)
1140 438a366a Michael Hanselmann
1141 438a366a Michael Hanselmann
        if (op == self.OP_RECV and
1142 438a366a Michael Hanselmann
            event & (select.POLLNVAL | select.POLLHUP | select.POLLERR)):
1143 438a366a Michael Hanselmann
          return ""
1144 438a366a Michael Hanselmann
1145 438a366a Michael Hanselmann
        if not event & wait_for_event:
1146 438a366a Michael Hanselmann
          continue
1147 438a366a Michael Hanselmann
1148 438a366a Michael Hanselmann
      # Reset override
1149 438a366a Michael Hanselmann
      event_override = 0
1150 438a366a Michael Hanselmann
1151 438a366a Michael Hanselmann
      try:
1152 438a366a Michael Hanselmann
        try:
1153 438a366a Michael Hanselmann
          if op == self.OP_SEND:
1154 438a366a Michael Hanselmann
            return self.sock.send(arg1)
1155 438a366a Michael Hanselmann
1156 438a366a Michael Hanselmann
          elif op in (self.OP_RECV, self.OP_CLOSE_CHECK):
1157 438a366a Michael Hanselmann
            return self.sock.recv(arg1)
1158 438a366a Michael Hanselmann
1159 438a366a Michael Hanselmann
          elif op == self.OP_SHUTDOWN:
1160 438a366a Michael Hanselmann
            if self._using_ssl:
1161 438a366a Michael Hanselmann
              # PyOpenSSL's shutdown() doesn't take arguments
1162 438a366a Michael Hanselmann
              return self.sock.shutdown()
1163 438a366a Michael Hanselmann
            else:
1164 438a366a Michael Hanselmann
              return self.sock.shutdown(arg1)
1165 438a366a Michael Hanselmann
1166 438a366a Michael Hanselmann
        except OpenSSL.SSL.WantWriteError:
1167 438a366a Michael Hanselmann
          # OpenSSL wants to write, poll for POLLOUT
1168 438a366a Michael Hanselmann
          event_override = select.POLLOUT
1169 438a366a Michael Hanselmann
          continue
1170 438a366a Michael Hanselmann
1171 438a366a Michael Hanselmann
        except OpenSSL.SSL.WantReadError:
1172 438a366a Michael Hanselmann
          # OpenSSL wants to read, poll for POLLIN
1173 438a366a Michael Hanselmann
          event_override = select.POLLIN | select.POLLPRI
1174 438a366a Michael Hanselmann
          continue
1175 438a366a Michael Hanselmann
1176 438a366a Michael Hanselmann
        except OpenSSL.SSL.WantX509LookupError:
1177 438a366a Michael Hanselmann
          continue
1178 438a366a Michael Hanselmann
1179 438a366a Michael Hanselmann
        except OpenSSL.SSL.SysCallError, err:
1180 438a366a Michael Hanselmann
          if op == self.OP_SEND:
1181 438a366a Michael Hanselmann
            # arg1 is the data when writing
1182 438a366a Michael Hanselmann
            if err.args and err.args[0] == -1 and arg1 == "":
1183 438a366a Michael Hanselmann
              # errors when writing empty strings are expected
1184 438a366a Michael Hanselmann
              # and can be ignored
1185 438a366a Michael Hanselmann
              return 0
1186 438a366a Michael Hanselmann
1187 438a366a Michael Hanselmann
          elif op == self.OP_RECV:
1188 438a366a Michael Hanselmann
            if err.args == (-1, _SSL_UNEXPECTED_EOF):
1189 438a366a Michael Hanselmann
              return ""
1190 438a366a Michael Hanselmann
1191 438a366a Michael Hanselmann
          raise socket.error(err.args)
1192 438a366a Michael Hanselmann
1193 438a366a Michael Hanselmann
        except OpenSSL.SSL.Error, err:
1194 438a366a Michael Hanselmann
          raise socket.error(err.args)
1195 438a366a Michael Hanselmann
1196 438a366a Michael Hanselmann
      except socket.error, err:
1197 438a366a Michael Hanselmann
        if err.args and err.args[0] == errno.EAGAIN:
1198 438a366a Michael Hanselmann
          # Ignore EAGAIN
1199 438a366a Michael Hanselmann
          continue
1200 438a366a Michael Hanselmann
1201 438a366a Michael Hanselmann
        raise _HttpClientError("%s: %s" % (error_msg, str(err)))
1202 438a366a Michael Hanselmann
1203 8a0b06d2 Michael Hanselmann
  def _Connect(self):
1204 8a0b06d2 Michael Hanselmann
    """Non-blocking connect to host with timeout.
1205 8a0b06d2 Michael Hanselmann

1206 8a0b06d2 Michael Hanselmann
    """
1207 8a0b06d2 Michael Hanselmann
    connected = False
1208 8a0b06d2 Michael Hanselmann
    while True:
1209 438a366a Michael Hanselmann
      try:
1210 438a366a Michael Hanselmann
        connect_error = self.sock.connect_ex((self.request.host,
1211 438a366a Michael Hanselmann
                                              self.request.port))
1212 438a366a Michael Hanselmann
      except socket.gaierror, err:
1213 438a366a Michael Hanselmann
        raise _HttpClientError("Connection failed: %s" % str(err))
1214 438a366a Michael Hanselmann
1215 8a0b06d2 Michael Hanselmann
      if connect_error == errno.EINTR:
1216 8a0b06d2 Michael Hanselmann
        # Mask signals
1217 8a0b06d2 Michael Hanselmann
        pass
1218 8a0b06d2 Michael Hanselmann
1219 8a0b06d2 Michael Hanselmann
      elif connect_error == 0:
1220 8a0b06d2 Michael Hanselmann
        # Connection established
1221 8a0b06d2 Michael Hanselmann
        connected = True
1222 8a0b06d2 Michael Hanselmann
        break
1223 8a0b06d2 Michael Hanselmann
1224 8a0b06d2 Michael Hanselmann
      elif connect_error == errno.EINPROGRESS:
1225 8a0b06d2 Michael Hanselmann
        # Connection started
1226 8a0b06d2 Michael Hanselmann
        break
1227 8a0b06d2 Michael Hanselmann
1228 438a366a Michael Hanselmann
      raise _HttpClientError("Connection failed (%s: %s)" %
1229 438a366a Michael Hanselmann
                             (connect_error, os.strerror(connect_error)))
1230 8a0b06d2 Michael Hanselmann
1231 8a0b06d2 Michael Hanselmann
    if not connected:
1232 8a0b06d2 Michael Hanselmann
      # Wait for connection
1233 8a0b06d2 Michael Hanselmann
      event = self._WaitForCondition(select.POLLOUT, self.CONNECT_TIMEOUT)
1234 8a0b06d2 Michael Hanselmann
      if event is None:
1235 438a366a Michael Hanselmann
        raise _HttpClientError("Timeout while connecting to server")
1236 8a0b06d2 Michael Hanselmann
1237 8a0b06d2 Michael Hanselmann
      # Get error code
1238 8a0b06d2 Michael Hanselmann
      connect_error = self.sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
1239 8a0b06d2 Michael Hanselmann
      if connect_error != 0:
1240 438a366a Michael Hanselmann
        raise _HttpClientError("Connection failed (%s: %s)" %
1241 438a366a Michael Hanselmann
                               (connect_error, os.strerror(connect_error)))
1242 8a0b06d2 Michael Hanselmann
1243 8a0b06d2 Michael Hanselmann
    # Enable TCP keep-alive
1244 8a0b06d2 Michael Hanselmann
    self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
1245 8a0b06d2 Michael Hanselmann
1246 8a0b06d2 Michael Hanselmann
    # If needed, Linux specific options are available to change the TCP
1247 8a0b06d2 Michael Hanselmann
    # keep-alive settings, see "man 7 tcp" for TCP_KEEPCNT, TCP_KEEPIDLE and
1248 8a0b06d2 Michael Hanselmann
    # TCP_KEEPINTVL.
1249 8a0b06d2 Michael Hanselmann
1250 8a0b06d2 Michael Hanselmann
  def _SendRequest(self):
1251 8a0b06d2 Michael Hanselmann
    """Sends request to server.
1252 8a0b06d2 Michael Hanselmann

1253 8a0b06d2 Michael Hanselmann
    """
1254 8a0b06d2 Michael Hanselmann
    buf = self._BuildRequest()
1255 8a0b06d2 Michael Hanselmann
1256 8a0b06d2 Michael Hanselmann
    while buf:
1257 438a366a Michael Hanselmann
      # Send only 4 KB at a time
1258 438a366a Michael Hanselmann
      data = buf[:4096]
1259 8a0b06d2 Michael Hanselmann
1260 438a366a Michael Hanselmann
      sent = self._SocketOperation(self.OP_SEND, data,
1261 438a366a Michael Hanselmann
                                   "Error while sending request",
1262 438a366a Michael Hanselmann
                                   "Timeout while sending request")
1263 8a0b06d2 Michael Hanselmann
1264 8a0b06d2 Michael Hanselmann
      # Remove sent bytes
1265 8a0b06d2 Michael Hanselmann
      buf = buf[sent:]
1266 8a0b06d2 Michael Hanselmann
1267 8a0b06d2 Michael Hanselmann
    assert not buf, "Request wasn't sent completely"
1268 8a0b06d2 Michael Hanselmann
1269 8a0b06d2 Michael Hanselmann
  def _ReadResponse(self):
1270 8a0b06d2 Michael Hanselmann
    """Read response from server.
1271 8a0b06d2 Michael Hanselmann

1272 8a0b06d2 Michael Hanselmann
    Calls the parser function after reading a chunk of data.
1273 8a0b06d2 Michael Hanselmann

1274 8a0b06d2 Michael Hanselmann
    """
1275 8a0b06d2 Michael Hanselmann
    buf = ""
1276 8a0b06d2 Michael Hanselmann
    eof = False
1277 8a0b06d2 Michael Hanselmann
    while self.parser_status != self.PS_COMPLETE:
1278 438a366a Michael Hanselmann
      data = self._SocketOperation(self.OP_RECV, 4096,
1279 438a366a Michael Hanselmann
                                   "Error while reading response",
1280 438a366a Michael Hanselmann
                                   "Timeout while reading response")
1281 8a0b06d2 Michael Hanselmann
1282 438a366a Michael Hanselmann
      if data:
1283 438a366a Michael Hanselmann
        buf += data
1284 438a366a Michael Hanselmann
      else:
1285 8a0b06d2 Michael Hanselmann
        eof = True
1286 8a0b06d2 Michael Hanselmann
1287 8a0b06d2 Michael Hanselmann
      # Do some parsing and error checking while more data arrives
1288 8a0b06d2 Michael Hanselmann
      buf = self._ParseBuffer(buf, eof)
1289 8a0b06d2 Michael Hanselmann
1290 8a0b06d2 Michael Hanselmann
      # Must be done only after the buffer has been evaluated
1291 8a0b06d2 Michael Hanselmann
      if (eof and
1292 8a0b06d2 Michael Hanselmann
          self.parser_status in (self.PS_STATUS_LINE,
1293 8a0b06d2 Michael Hanselmann
                                 self.PS_HEADERS)):
1294 438a366a Michael Hanselmann
        raise _HttpClientError("Connection closed prematurely")
1295 8a0b06d2 Michael Hanselmann
1296 8a0b06d2 Michael Hanselmann
    # Parse rest
1297 8a0b06d2 Michael Hanselmann
    buf = self._ParseBuffer(buf, True)
1298 8a0b06d2 Michael Hanselmann
1299 8a0b06d2 Michael Hanselmann
    assert self.parser_status == self.PS_COMPLETE
1300 8a0b06d2 Michael Hanselmann
    assert not buf, "Parser didn't read full response"
1301 8a0b06d2 Michael Hanselmann
1302 8a0b06d2 Michael Hanselmann
  def _CloseConnection(self, force):
1303 8a0b06d2 Michael Hanselmann
    """Closes the connection.
1304 8a0b06d2 Michael Hanselmann

1305 8a0b06d2 Michael Hanselmann
    """
1306 8a0b06d2 Michael Hanselmann
    if self.server_will_close and not force:
1307 8a0b06d2 Michael Hanselmann
      # Wait for server to close
1308 438a366a Michael Hanselmann
      try:
1309 438a366a Michael Hanselmann
        # Check whether it's actually closed
1310 438a366a Michael Hanselmann
        if not self._SocketOperation(self.OP_CLOSE_CHECK, 1,
1311 438a366a Michael Hanselmann
                                     "Error", "Timeout"):
1312 438a366a Michael Hanselmann
          return
1313 438a366a Michael Hanselmann
      except (socket.error, _HttpClientError):
1314 438a366a Michael Hanselmann
        # Ignore errors at this stage
1315 8a0b06d2 Michael Hanselmann
        pass
1316 8a0b06d2 Michael Hanselmann
1317 8a0b06d2 Michael Hanselmann
    # Close the connection from our side
1318 438a366a Michael Hanselmann
    self._SocketOperation(self.OP_SHUTDOWN, socket.SHUT_RDWR,
1319 438a366a Michael Hanselmann
                          "Error while shutting down connection",
1320 438a366a Michael Hanselmann
                          "Timeout while shutting down connection")
1321 8a0b06d2 Michael Hanselmann
1322 8a0b06d2 Michael Hanselmann
1323 33bbdbec Michael Hanselmann
class _HttpClientPendingRequest(object):
1324 33bbdbec Michael Hanselmann
  """Data class for pending requests.
1325 33bbdbec Michael Hanselmann

1326 33bbdbec Michael Hanselmann
  """
1327 33bbdbec Michael Hanselmann
  def __init__(self, request):
1328 33bbdbec Michael Hanselmann
    self.request = request
1329 33bbdbec Michael Hanselmann
1330 33bbdbec Michael Hanselmann
    # Thread synchronization
1331 33bbdbec Michael Hanselmann
    self.done = threading.Event()
1332 33bbdbec Michael Hanselmann
1333 33bbdbec Michael Hanselmann
1334 8a0b06d2 Michael Hanselmann
class HttpClientWorker(workerpool.BaseWorker):
1335 8a0b06d2 Michael Hanselmann
  """HTTP client worker class.
1336 8a0b06d2 Michael Hanselmann

1337 8a0b06d2 Michael Hanselmann
  """
1338 33bbdbec Michael Hanselmann
  def RunTask(self, pend_req):
1339 33bbdbec Michael Hanselmann
    try:
1340 33bbdbec Michael Hanselmann
      HttpClientRequestExecutor(pend_req.request)
1341 33bbdbec Michael Hanselmann
    finally:
1342 33bbdbec Michael Hanselmann
      pend_req.done.set()
1343 8a0b06d2 Michael Hanselmann
1344 8a0b06d2 Michael Hanselmann
1345 8a0b06d2 Michael Hanselmann
class HttpClientWorkerPool(workerpool.WorkerPool):
1346 8a0b06d2 Michael Hanselmann
  def __init__(self, manager):
1347 8a0b06d2 Michael Hanselmann
    workerpool.WorkerPool.__init__(self, HTTP_CLIENT_THREADS,
1348 8a0b06d2 Michael Hanselmann
                                   HttpClientWorker)
1349 8a0b06d2 Michael Hanselmann
    self.manager = manager
1350 8a0b06d2 Michael Hanselmann
1351 8a0b06d2 Michael Hanselmann
1352 8a0b06d2 Michael Hanselmann
class HttpClientManager(object):
1353 33bbdbec Michael Hanselmann
  """Manages HTTP requests.
1354 33bbdbec Michael Hanselmann

1355 33bbdbec Michael Hanselmann
  """
1356 8a0b06d2 Michael Hanselmann
  def __init__(self):
1357 8a0b06d2 Michael Hanselmann
    self._wpool = HttpClientWorkerPool(self)
1358 8a0b06d2 Michael Hanselmann
1359 8a0b06d2 Michael Hanselmann
  def __del__(self):
1360 8a0b06d2 Michael Hanselmann
    self.Shutdown()
1361 8a0b06d2 Michael Hanselmann
1362 8a0b06d2 Michael Hanselmann
  def ExecRequests(self, requests):
1363 33bbdbec Michael Hanselmann
    """Execute HTTP requests.
1364 8a0b06d2 Michael Hanselmann

1365 33bbdbec Michael Hanselmann
    This function can be called from multiple threads at the same time.
1366 8a0b06d2 Michael Hanselmann

1367 33bbdbec Michael Hanselmann
    @type requests: List of HttpClientRequest instances
1368 33bbdbec Michael Hanselmann
    @param requests: The requests to execute
1369 33bbdbec Michael Hanselmann
    @rtype: List of HttpClientRequest instances
1370 33bbdbec Michael Hanselmann
    @returns: The list of requests passed in
1371 33bbdbec Michael Hanselmann

1372 33bbdbec Michael Hanselmann
    """
1373 33bbdbec Michael Hanselmann
    # _HttpClientPendingRequest is used for internal thread synchronization
1374 33bbdbec Michael Hanselmann
    pending = [_HttpClientPendingRequest(req) for req in requests]
1375 33bbdbec Michael Hanselmann
1376 33bbdbec Michael Hanselmann
    try:
1377 33bbdbec Michael Hanselmann
      # Add requests to queue
1378 33bbdbec Michael Hanselmann
      for pend_req in pending:
1379 33bbdbec Michael Hanselmann
        self._wpool.AddTask(pend_req)
1380 33bbdbec Michael Hanselmann
1381 33bbdbec Michael Hanselmann
    finally:
1382 33bbdbec Michael Hanselmann
      # In case of an exception we should still wait for the rest, otherwise
1383 33bbdbec Michael Hanselmann
      # another thread from the worker pool could modify the request object
1384 33bbdbec Michael Hanselmann
      # after we returned.
1385 33bbdbec Michael Hanselmann
1386 33bbdbec Michael Hanselmann
      # And wait for them to finish
1387 33bbdbec Michael Hanselmann
      for pend_req in pending:
1388 33bbdbec Michael Hanselmann
        pend_req.done.wait()
1389 33bbdbec Michael Hanselmann
1390 33bbdbec Michael Hanselmann
    # Return original list
1391 8a0b06d2 Michael Hanselmann
    return requests
1392 8a0b06d2 Michael Hanselmann
1393 8a0b06d2 Michael Hanselmann
  def Shutdown(self):
1394 33bbdbec Michael Hanselmann
    self._wpool.Quiesce()
1395 8a0b06d2 Michael Hanselmann
    self._wpool.TerminateWorkers()
1396 8a0b06d2 Michael Hanselmann
1397 8a0b06d2 Michael Hanselmann
1398 42242313 Michael Hanselmann
class _SSLFileObject(object):
1399 42242313 Michael Hanselmann
  """Wrapper around socket._fileobject
1400 42242313 Michael Hanselmann

1401 42242313 Michael Hanselmann
  This wrapper is required to handle OpenSSL exceptions.
1402 42242313 Michael Hanselmann

1403 42242313 Michael Hanselmann
  """
1404 42242313 Michael Hanselmann
  def _RequireOpenSocket(fn):
1405 42242313 Michael Hanselmann
    def wrapper(self, *args, **kwargs):
1406 42242313 Michael Hanselmann
      if self.closed:
1407 42242313 Michael Hanselmann
        raise SocketClosed("Socket is closed")
1408 42242313 Michael Hanselmann
      return fn(self, *args, **kwargs)
1409 42242313 Michael Hanselmann
    return wrapper
1410 42242313 Michael Hanselmann
1411 42242313 Michael Hanselmann
  def __init__(self, sock, mode='rb', bufsize=-1):
1412 42242313 Michael Hanselmann
    self._base = socket._fileobject(sock, mode=mode, bufsize=bufsize)
1413 42242313 Michael Hanselmann
1414 42242313 Michael Hanselmann
  def _ConnectionLost(self):
1415 42242313 Michael Hanselmann
    self._base = None
1416 42242313 Michael Hanselmann
1417 42242313 Michael Hanselmann
  def _getclosed(self):
1418 42242313 Michael Hanselmann
    return self._base is None or self._base.closed
1419 42242313 Michael Hanselmann
  closed = property(_getclosed, doc="True if the file is closed")
1420 42242313 Michael Hanselmann
1421 42242313 Michael Hanselmann
  @_RequireOpenSocket
1422 42242313 Michael Hanselmann
  def close(self):
1423 42242313 Michael Hanselmann
    return self._base.close()
1424 42242313 Michael Hanselmann
1425 42242313 Michael Hanselmann
  @_RequireOpenSocket
1426 42242313 Michael Hanselmann
  def flush(self):
1427 42242313 Michael Hanselmann
    return self._base.flush()
1428 42242313 Michael Hanselmann
1429 42242313 Michael Hanselmann
  @_RequireOpenSocket
1430 42242313 Michael Hanselmann
  def fileno(self):
1431 42242313 Michael Hanselmann
    return self._base.fileno()
1432 42242313 Michael Hanselmann
1433 42242313 Michael Hanselmann
  @_RequireOpenSocket
1434 42242313 Michael Hanselmann
  def read(self, size=-1):
1435 42242313 Michael Hanselmann
    return self._ReadWrapper(self._base.read, size=size)
1436 42242313 Michael Hanselmann
1437 42242313 Michael Hanselmann
  @_RequireOpenSocket
1438 42242313 Michael Hanselmann
  def readline(self, size=-1):
1439 42242313 Michael Hanselmann
    return self._ReadWrapper(self._base.readline, size=size)
1440 42242313 Michael Hanselmann
1441 42242313 Michael Hanselmann
  def _ReadWrapper(self, fn, *args, **kwargs):
1442 42242313 Michael Hanselmann
    while True:
1443 42242313 Michael Hanselmann
      try:
1444 42242313 Michael Hanselmann
        return fn(*args, **kwargs)
1445 42242313 Michael Hanselmann
1446 42242313 Michael Hanselmann
      except OpenSSL.SSL.ZeroReturnError, err:
1447 42242313 Michael Hanselmann
        self._ConnectionLost()
1448 42242313 Michael Hanselmann
        return ""
1449 42242313 Michael Hanselmann
1450 42242313 Michael Hanselmann
      except OpenSSL.SSL.WantReadError:
1451 42242313 Michael Hanselmann
        continue
1452 42242313 Michael Hanselmann
1453 42242313 Michael Hanselmann
      #except OpenSSL.SSL.WantWriteError:
1454 42242313 Michael Hanselmann
      # TODO
1455 42242313 Michael Hanselmann
1456 42242313 Michael Hanselmann
      except OpenSSL.SSL.SysCallError, (retval, desc):
1457 d7bace1b Michael Hanselmann
        if ((retval == -1 and desc == _SSL_UNEXPECTED_EOF)
1458 42242313 Michael Hanselmann
            or retval > 0):
1459 42242313 Michael Hanselmann
          self._ConnectionLost()
1460 42242313 Michael Hanselmann
          return ""
1461 42242313 Michael Hanselmann
1462 42242313 Michael Hanselmann
        logging.exception("Error in OpenSSL")
1463 42242313 Michael Hanselmann
        self._ConnectionLost()
1464 42242313 Michael Hanselmann
        raise socket.error(err.args)
1465 42242313 Michael Hanselmann
1466 42242313 Michael Hanselmann
      except OpenSSL.SSL.Error, err:
1467 42242313 Michael Hanselmann
        self._ConnectionLost()
1468 42242313 Michael Hanselmann
        raise socket.error(err.args)
1469 42242313 Michael Hanselmann
1470 42242313 Michael Hanselmann
  @_RequireOpenSocket
1471 42242313 Michael Hanselmann
  def write(self, data):
1472 42242313 Michael Hanselmann
    return self._WriteWrapper(self._base.write, data)
1473 42242313 Michael Hanselmann
1474 42242313 Michael Hanselmann
  def _WriteWrapper(self, fn, *args, **kwargs):
1475 42242313 Michael Hanselmann
    while True:
1476 42242313 Michael Hanselmann
      try:
1477 42242313 Michael Hanselmann
        return fn(*args, **kwargs)
1478 42242313 Michael Hanselmann
      except OpenSSL.SSL.ZeroReturnError, err:
1479 42242313 Michael Hanselmann
        self._ConnectionLost()
1480 42242313 Michael Hanselmann
        return 0
1481 42242313 Michael Hanselmann
1482 42242313 Michael Hanselmann
      except OpenSSL.SSL.WantWriteError:
1483 42242313 Michael Hanselmann
        continue
1484 42242313 Michael Hanselmann
1485 42242313 Michael Hanselmann
      #except OpenSSL.SSL.WantReadError:
1486 42242313 Michael Hanselmann
      # TODO
1487 42242313 Michael Hanselmann
1488 42242313 Michael Hanselmann
      except OpenSSL.SSL.SysCallError, err:
1489 42242313 Michael Hanselmann
        if err.args[0] == -1 and data == "":
1490 42242313 Michael Hanselmann
          # errors when writing empty strings are expected
1491 42242313 Michael Hanselmann
          # and can be ignored
1492 42242313 Michael Hanselmann
          return 0
1493 42242313 Michael Hanselmann
1494 42242313 Michael Hanselmann
        self._ConnectionLost()
1495 42242313 Michael Hanselmann
        raise socket.error(err.args)
1496 42242313 Michael Hanselmann
1497 42242313 Michael Hanselmann
      except OpenSSL.SSL.Error, err:
1498 42242313 Michael Hanselmann
        self._ConnectionLost()
1499 42242313 Michael Hanselmann
        raise socket.error(err.args)