Statistics
| Branch: | Tag: | Revision:

root / lib / http.py @ a1578d63

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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