# Copyright 2018 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import BaseHTTPServer import thread import urlparse def _split_url(url): """Splits a URL into the URL base and path.""" split_url = urlparse.urlsplit(url) url_base = urlparse.urlunsplit( (split_url.scheme, split_url.netloc, '', '', '')) url_path = split_url.path return url_base, url_path.lstrip('/') class NanoOmahaDevserver(object): """A simple Omaha instance that can be setup on a DUT in client tests.""" def __init__(self, eol=False): """ Create a nano omaha devserver. @param eol: True if we should return a response with _eol flag """ self._eol = eol class Handler(BaseHTTPServer.BaseHTTPRequestHandler): """Inner class for handling HTTP requests.""" _OMAHA_RESPONSE_TEMPLATE_HEAD = """ <response protocol=\"3.0\"> <daystart elapsed_seconds=\"44801\"/> <app appid=\"{87efface-864d-49a5-9bb3-4b050a7c227a}\" status=\"ok\"> <ping status=\"ok\"/> <updatecheck status=\"ok\"> <urls> <url codebase=\"%s\"/> </urls> <manifest version=\"9999.0.0\"> <packages> <package name=\"%s\" size=\"%d\" required=\"true\"/> </packages> <actions> <action event=\"postinstall\" ChromeOSVersion=\"9999.0.0\" sha256=\"%s\" needsadmin=\"false\" IsDeltaPayload=\"%s\" """ _OMAHA_RESPONSE_TEMPLATE_TAIL = """ /> </actions> </manifest> </updatecheck> </app> </response> """ _OMAHA_RESPONSE_EOL = """ <response protocol=\"3.0\"> <daystart elapsed_seconds=\"44801\"/> <app appid=\"{87efface-864d-49a5-9bb3-4b050a7c227a}\" status=\"ok\"> <ping status=\"ok\"/> <updatecheck _eol=\"eol\" status=\"noupdate\"/> </app> </response> """ def do_POST(self): """Handler for POST requests.""" if self.path == '/update': if self.server._devserver._eol: response = self._OMAHA_RESPONSE_EOL else: (base, name) = _split_url(self.server._devserver._image_url) response = self._OMAHA_RESPONSE_TEMPLATE_HEAD % ( base + '/', name, self.server._devserver._image_size, self.server._devserver._sha256, str(self.server._devserver._is_delta).lower()) if self.server._devserver._is_delta: response += ' IsDelta="true"\n' if self.server._devserver._critical: response += ' deadline="now"\n' if self.server._devserver._metadata_size: response += ' MetadataSize="%d"\n' % ( self.server._devserver._metadata_size) if self.server._devserver._metadata_signature: response += ' ' \ 'MetadataSignatureRsa="%s"\n' % ( self.server._devserver._metadata_signature) if self.server._devserver._public_key: response += ' PublicKeyRsa="%s"\n' % ( self.server._devserver._public_key) response += self._OMAHA_RESPONSE_TEMPLATE_TAIL self.send_response(200) self.send_header('Content-Type', 'application/xml') self.end_headers() self.wfile.write(response) else: self.send_response(500) def start(self): """Starts the server.""" self._httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', 0), self.Handler) self._httpd._devserver = self # Serve HTTP requests in a dedicated thread. thread.start_new_thread(self._httpd.serve_forever, ()) self._port = self._httpd.socket.getsockname()[1] def stop(self): """Stops the server.""" self._httpd.shutdown() def get_port(self): """Returns the TCP port number the server is listening on.""" return self._port def set_image_params(self, image_url, image_size, sha256, metadata_size=None, metadata_signature=None, public_key=None, is_delta=False, critical=False): """Sets the values to return in the Omaha response. Only the |image_url|, |image_size| and |sha256| parameters are mandatory.""" self._image_url = image_url self._image_size = image_size self._sha256 = sha256 self._metadata_size = metadata_size self._metadata_signature = metadata_signature self._public_key = public_key self._is_delta = is_delta self._critical = critical