Statistics
| Branch: | Tag: | Revision:

root / lib / http.py @ e873317a

History | View | Annotate | Download (6.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 socket
23 a43f68dc Michael Hanselmann
import BaseHTTPServer
24 a43f68dc Michael Hanselmann
import OpenSSL
25 a43f68dc Michael Hanselmann
import time
26 a43f68dc Michael Hanselmann
import logging
27 a43f68dc Michael Hanselmann
28 a43f68dc Michael Hanselmann
from ganeti import errors
29 a43f68dc Michael Hanselmann
from ganeti import logger
30 a43f68dc Michael Hanselmann
from ganeti import serializer
31 a43f68dc Michael Hanselmann
32 a43f68dc Michael Hanselmann
33 a43f68dc Michael Hanselmann
class HTTPException(Exception):
34 a43f68dc Michael Hanselmann
  code = None
35 a43f68dc Michael Hanselmann
  message = None
36 a43f68dc Michael Hanselmann
37 a43f68dc Michael Hanselmann
  def __init__(self, message=None):
38 a43f68dc Michael Hanselmann
    if message is not None:
39 a43f68dc Michael Hanselmann
      self.message = message
40 a43f68dc Michael Hanselmann
41 a43f68dc Michael Hanselmann
42 a43f68dc Michael Hanselmann
class HTTPBadRequest(HTTPException):
43 a43f68dc Michael Hanselmann
  code = 400
44 a43f68dc Michael Hanselmann
45 a43f68dc Michael Hanselmann
46 a43f68dc Michael Hanselmann
class HTTPForbidden(HTTPException):
47 a43f68dc Michael Hanselmann
  code = 403
48 a43f68dc Michael Hanselmann
49 a43f68dc Michael Hanselmann
50 a43f68dc Michael Hanselmann
class HTTPNotFound(HTTPException):
51 a43f68dc Michael Hanselmann
  code = 404
52 a43f68dc Michael Hanselmann
53 a43f68dc Michael Hanselmann
54 a43f68dc Michael Hanselmann
class HTTPGone(HTTPException):
55 a43f68dc Michael Hanselmann
  code = 410
56 a43f68dc Michael Hanselmann
57 a43f68dc Michael Hanselmann
58 a43f68dc Michael Hanselmann
class HTTPLengthRequired(HTTPException):
59 a43f68dc Michael Hanselmann
  code = 411
60 a43f68dc Michael Hanselmann
61 a43f68dc Michael Hanselmann
62 a43f68dc Michael Hanselmann
class HTTPInternalError(HTTPException):
63 a43f68dc Michael Hanselmann
  code = 500
64 a43f68dc Michael Hanselmann
65 a43f68dc Michael Hanselmann
66 a43f68dc Michael Hanselmann
class HTTPNotImplemented(HTTPException):
67 a43f68dc Michael Hanselmann
  code = 501
68 a43f68dc Michael Hanselmann
69 a43f68dc Michael Hanselmann
70 a43f68dc Michael Hanselmann
class HTTPServiceUnavailable(HTTPException):
71 a43f68dc Michael Hanselmann
  code = 503
72 a43f68dc Michael Hanselmann
73 a43f68dc Michael Hanselmann
74 a43f68dc Michael Hanselmann
class ApacheLogfile:
75 a43f68dc Michael Hanselmann
  """Utility class to write HTTP server log files.
76 a43f68dc Michael Hanselmann

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

80 a43f68dc Michael Hanselmann
  """
81 a43f68dc Michael Hanselmann
  MONTHNAME = [None,
82 a43f68dc Michael Hanselmann
               'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
83 a43f68dc Michael Hanselmann
               'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
84 a43f68dc Michael Hanselmann
85 a43f68dc Michael Hanselmann
  def __init__(self, fd):
86 a43f68dc Michael Hanselmann
    """Constructor for ApacheLogfile class.
87 a43f68dc Michael Hanselmann

88 a43f68dc Michael Hanselmann
    Args:
89 a43f68dc Michael Hanselmann
    - fd: Open file object
90 a43f68dc Michael Hanselmann

91 a43f68dc Michael Hanselmann
    """
92 a43f68dc Michael Hanselmann
    self._fd = fd
93 a43f68dc Michael Hanselmann
94 a43f68dc Michael Hanselmann
  def LogRequest(self, request, format, *args):
95 a43f68dc Michael Hanselmann
    self._fd.write("%s %s %s [%s] %s\n" % (
96 a43f68dc Michael Hanselmann
      # Remote host address
97 a43f68dc Michael Hanselmann
      request.address_string(),
98 a43f68dc Michael Hanselmann
99 a43f68dc Michael Hanselmann
      # RFC1413 identity (identd)
100 a43f68dc Michael Hanselmann
      "-",
101 a43f68dc Michael Hanselmann
102 a43f68dc Michael Hanselmann
      # Remote user
103 a43f68dc Michael Hanselmann
      "-",
104 a43f68dc Michael Hanselmann
105 a43f68dc Michael Hanselmann
      # Request time
106 a43f68dc Michael Hanselmann
      self._FormatCurrentTime(),
107 a43f68dc Michael Hanselmann
108 a43f68dc Michael Hanselmann
      # Message
109 a43f68dc Michael Hanselmann
      format % args,
110 a43f68dc Michael Hanselmann
      ))
111 a0638838 Oleksiy Mishchenko
    self._fd.flush()
112 a43f68dc Michael Hanselmann
113 a43f68dc Michael Hanselmann
  def _FormatCurrentTime(self):
114 a43f68dc Michael Hanselmann
    """Formats current time in Common Log Format.
115 a43f68dc Michael Hanselmann

116 a43f68dc Michael Hanselmann
    """
117 a43f68dc Michael Hanselmann
    return self._FormatLogTime(time.time())
118 a43f68dc Michael Hanselmann
119 a43f68dc Michael Hanselmann
  def _FormatLogTime(self, seconds):
120 a43f68dc Michael Hanselmann
    """Formats time for Common Log Format.
121 a43f68dc Michael Hanselmann

122 a43f68dc Michael Hanselmann
    All timestamps are logged in the UTC timezone.
123 a43f68dc Michael Hanselmann

124 a43f68dc Michael Hanselmann
    Args:
125 a43f68dc Michael Hanselmann
    - seconds: Time in seconds since the epoch
126 a43f68dc Michael Hanselmann

127 a43f68dc Michael Hanselmann
    """
128 a43f68dc Michael Hanselmann
    (_, month, _, _, _, _, _, _, _) = tm = time.gmtime(seconds)
129 a43f68dc Michael Hanselmann
    format = "%d/" + self.MONTHNAME[month] + "/%Y:%H:%M:%S +0000"
130 a43f68dc Michael Hanselmann
    return time.strftime(format, tm)
131 a43f68dc Michael Hanselmann
132 a43f68dc Michael Hanselmann
133 a43f68dc Michael Hanselmann
class HTTPServer(BaseHTTPServer.HTTPServer, object):
134 a43f68dc Michael Hanselmann
  """Class to provide an HTTP/HTTPS server.
135 a43f68dc Michael Hanselmann

136 a43f68dc Michael Hanselmann
  """
137 a43f68dc Michael Hanselmann
  allow_reuse_address = True
138 a43f68dc Michael Hanselmann
139 a43f68dc Michael Hanselmann
  def __init__(self, server_address, HandlerClass, httplog=None,
140 a43f68dc Michael Hanselmann
               enable_ssl=False, ssl_key=None, ssl_cert=None):
141 a43f68dc Michael Hanselmann
    """Server constructor.
142 a43f68dc Michael Hanselmann

143 a43f68dc Michael Hanselmann
    Args:
144 a43f68dc Michael Hanselmann
      server_address: a touple containing:
145 a43f68dc Michael Hanselmann
        ip: a string with IP address, localhost if empty string
146 a43f68dc Michael Hanselmann
        port: port number, integer
147 a43f68dc Michael Hanselmann
      HandlerClass: HTTPRequestHandler object
148 a43f68dc Michael Hanselmann
      httplog: Access log object
149 a43f68dc Michael Hanselmann
      enable_ssl: Whether to enable SSL
150 a43f68dc Michael Hanselmann
      ssl_key: SSL key file
151 a43f68dc Michael Hanselmann
      ssl_cert: SSL certificate key
152 a43f68dc Michael Hanselmann

153 a43f68dc Michael Hanselmann
    """
154 a43f68dc Michael Hanselmann
    BaseHTTPServer.HTTPServer.__init__(self, server_address, HandlerClass)
155 a43f68dc Michael Hanselmann
156 a43f68dc Michael Hanselmann
    self.httplog = httplog
157 a43f68dc Michael Hanselmann
158 a43f68dc Michael Hanselmann
    if enable_ssl:
159 a43f68dc Michael Hanselmann
      # Set up SSL
160 a43f68dc Michael Hanselmann
      context = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
161 a43f68dc Michael Hanselmann
      context.use_privatekey_file(ssl_key)
162 a43f68dc Michael Hanselmann
      context.use_certificate_file(ssl_cert)
163 a43f68dc Michael Hanselmann
      self.socket = OpenSSL.SSL.Connection(context,
164 a43f68dc Michael Hanselmann
                                           socket.socket(self.address_family,
165 a43f68dc Michael Hanselmann
                                           self.socket_type))
166 a43f68dc Michael Hanselmann
    else:
167 a43f68dc Michael Hanselmann
      self.socket = socket.socket(self.address_family, self.socket_type)
168 a43f68dc Michael Hanselmann
169 a43f68dc Michael Hanselmann
    self.server_bind()
170 a43f68dc Michael Hanselmann
    self.server_activate()
171 a43f68dc Michael Hanselmann
172 a43f68dc Michael Hanselmann
173 a43f68dc Michael Hanselmann
class HTTPJsonConverter:
174 a43f68dc Michael Hanselmann
  CONTENT_TYPE = "application/json"
175 a43f68dc Michael Hanselmann
176 a43f68dc Michael Hanselmann
  def Encode(self, data):
177 a43f68dc Michael Hanselmann
    return serializer.DumpJson(data)
178 a43f68dc Michael Hanselmann
179 a43f68dc Michael Hanselmann
  def Decode(self, data):
180 a43f68dc Michael Hanselmann
    return serializer.LoadJson(data)
181 a43f68dc Michael Hanselmann
182 a43f68dc Michael Hanselmann
183 a43f68dc Michael Hanselmann
class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler, object):
184 a43f68dc Michael Hanselmann
  """Request handler class.
185 a43f68dc Michael Hanselmann

186 a43f68dc Michael Hanselmann
  """
187 a43f68dc Michael Hanselmann
  def setup(self):
188 a43f68dc Michael Hanselmann
    """Setup secure read and write file objects.
189 a43f68dc Michael Hanselmann

190 a43f68dc Michael Hanselmann
    """
191 a43f68dc Michael Hanselmann
    self.connection = self.request
192 a43f68dc Michael Hanselmann
    self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
193 a43f68dc Michael Hanselmann
    self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
194 a43f68dc Michael Hanselmann
195 a43f68dc Michael Hanselmann
  def handle_one_request(self):
196 a43f68dc Michael Hanselmann
    """Parses a request and calls the handler function.
197 a43f68dc Michael Hanselmann

198 a43f68dc Michael Hanselmann
    """
199 a43f68dc Michael Hanselmann
    self.raw_requestline = None
200 a43f68dc Michael Hanselmann
    try:
201 a43f68dc Michael Hanselmann
      self.raw_requestline = self.rfile.readline()
202 a43f68dc Michael Hanselmann
    except OpenSSL.SSL.Error, ex:
203 a43f68dc Michael Hanselmann
      logger.Error("Error in SSL: %s" % str(ex))
204 a43f68dc Michael Hanselmann
    if not self.raw_requestline:
205 a43f68dc Michael Hanselmann
      self.close_connection = 1
206 a43f68dc Michael Hanselmann
      return
207 a43f68dc Michael Hanselmann
    if not self.parse_request(): # An error code has been sent, just exit
208 a43f68dc Michael Hanselmann
      return
209 a43f68dc Michael Hanselmann
    logging.debug("HTTP request: %s", self.raw_requestline.rstrip("\r\n"))
210 a43f68dc Michael Hanselmann
211 a43f68dc Michael Hanselmann
    try:
212 a43f68dc Michael Hanselmann
      self._ReadPostData()
213 a43f68dc Michael Hanselmann
214 a43f68dc Michael Hanselmann
      result = self.HandleRequest()
215 a43f68dc Michael Hanselmann
216 a43f68dc Michael Hanselmann
      # TODO: Content-type
217 a43f68dc Michael Hanselmann
      encoder = HTTPJsonConverter()
218 a43f68dc Michael Hanselmann
      encoded_result = encoder.Encode(result)
219 a43f68dc Michael Hanselmann
220 a43f68dc Michael Hanselmann
      self.send_response(200)
221 a43f68dc Michael Hanselmann
      self.send_header("Content-Type", encoder.CONTENT_TYPE)
222 a43f68dc Michael Hanselmann
      self.send_header("Content-Length", str(len(encoded_result)))
223 a43f68dc Michael Hanselmann
      self.end_headers()
224 a43f68dc Michael Hanselmann
225 a43f68dc Michael Hanselmann
      self.wfile.write(encoded_result)
226 a43f68dc Michael Hanselmann
227 a43f68dc Michael Hanselmann
    except HTTPException, err:
228 a43f68dc Michael Hanselmann
      self.send_error(err.code, message=err.message)
229 a43f68dc Michael Hanselmann
230 a43f68dc Michael Hanselmann
    except Exception, err:
231 a43f68dc Michael Hanselmann
      self.send_error(HTTPInternalError.code, message=str(err))
232 a43f68dc Michael Hanselmann
233 a43f68dc Michael Hanselmann
    except:
234 a43f68dc Michael Hanselmann
      self.send_error(HTTPInternalError.code, message="Unknown error")
235 a43f68dc Michael Hanselmann
236 a43f68dc Michael Hanselmann
  def _ReadPostData(self):
237 a43f68dc Michael Hanselmann
    if self.command.upper() not in ("POST", "PUT"):
238 a43f68dc Michael Hanselmann
      self.post_data = None
239 a43f68dc Michael Hanselmann
      return
240 a43f68dc Michael Hanselmann
241 a43f68dc Michael Hanselmann
    # TODO: Decide what to do when Content-Length header was not sent
242 a43f68dc Michael Hanselmann
    try:
243 a43f68dc Michael Hanselmann
      content_length = int(self.headers.get('Content-Length', 0))
244 a43f68dc Michael Hanselmann
    except ValueError:
245 a43f68dc Michael Hanselmann
      raise HTTPBadRequest("No Content-Length header or invalid format")
246 a43f68dc Michael Hanselmann
247 a43f68dc Michael Hanselmann
    try:
248 a43f68dc Michael Hanselmann
      data = self.rfile.read(content_length)
249 a43f68dc Michael Hanselmann
    except socket.error, err:
250 a43f68dc Michael Hanselmann
      logger.Error("Socket error while reading: %s" % str(err))
251 a43f68dc Michael Hanselmann
      return
252 a43f68dc Michael Hanselmann
253 a43f68dc Michael Hanselmann
    # TODO: Content-type, error handling
254 a43f68dc Michael Hanselmann
    self.post_data = HTTPJsonConverter().Decode(data)
255 a43f68dc Michael Hanselmann
256 a43f68dc Michael Hanselmann
    logging.debug("HTTP POST data: %s", self.post_data)
257 a43f68dc Michael Hanselmann
258 a43f68dc Michael Hanselmann
  def HandleRequest(self):
259 a43f68dc Michael Hanselmann
    """Handles a request.
260 a43f68dc Michael Hanselmann

261 a43f68dc Michael Hanselmann
    """
262 a43f68dc Michael Hanselmann
    raise NotImplementedError()
263 a43f68dc Michael Hanselmann
264 a43f68dc Michael Hanselmann
  def log_message(self, format, *args):
265 a43f68dc Michael Hanselmann
    """Log an arbitrary message.
266 a43f68dc Michael Hanselmann

267 a43f68dc Michael Hanselmann
    This is used by all other logging functions.
268 a43f68dc Michael Hanselmann

269 a43f68dc Michael Hanselmann
    The first argument, FORMAT, is a format string for the
270 a43f68dc Michael Hanselmann
    message to be logged.  If the format string contains
271 a43f68dc Michael Hanselmann
    any % escapes requiring parameters, they should be
272 a43f68dc Michael Hanselmann
    specified as subsequent arguments (it's just like
273 a43f68dc Michael Hanselmann
    printf!).
274 a43f68dc Michael Hanselmann

275 a43f68dc Michael Hanselmann
    """
276 a43f68dc Michael Hanselmann
    logging.debug("Handled request: %s", format % args)
277 a43f68dc Michael Hanselmann
    if self.server.httplog:
278 a43f68dc Michael Hanselmann
      self.server.httplog.LogRequest(self, format, *args)