http_server.py 5.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#   Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Http Server."""

import logging

# NOTE: HTTPServer has a different name in python2 and python3
T
tianshuo78520a 已提交
19 20
from http.server import HTTPServer
import http.server as SimpleHTTPServer
21 22 23

import threading

24 25
__all__ = []

26 27 28 29 30 31 32 33

def get_logger(name, level, fmt):
    logger = logging.getLogger(name)
    logger.setLevel(level)
    handler = logging.FileHandler('http.log', mode='w')
    formatter = logging.Formatter(fmt=fmt)
    handler.setFormatter(formatter)
    logger.addHandler(handler)
34
    logger.propagate = False
35 36 37
    return logger


38 39 40
_http_server_logger = get_logger(
    __name__, logging.INFO, fmt='%(asctime)s-%(levelname)s: %(message)s'
)
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110


class KVHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    """
    kv handler class for kv http server,
    it defines the way to get/set kv in server.
    """

    def do_GET(self):
        """
        get method for kv handler, get value according to key.
        """
        log_str = "GET " + self.address_string() + self.path
        paths = self.path.split('/')
        if len(paths) < 3:
            print('len of request path must be 3: ' + self.path)
            self.send_status_code(400)
            return
        _, scope, key = paths
        with self.server.kv_lock:
            value = self.server.kv.get(scope, {}).get(key)
        if value is None:
            log_str += ' , key not found: ' + key
            self.send_status_code(404)
        else:
            log_str += ' , key found: ' + key
            self.send_response(200)
            self.send_header("Content-Length", str(len(value)))
            self.end_headers()
            self.wfile.write(value)
        _http_server_logger.info(log_str)

    def do_PUT(self):
        """
        put method for kv handler, set value according to key.
        """
        log_str = "PUT " + self.address_string() + self.path
        paths = self.path.split('/')
        if len(paths) < 3:
            print('len of request path must be 3: ' + self.path)
            self.send_status_code(400)
            return
        _, scope, key = paths
        content_length = int(self.headers['Content-Length'])
        try:
            value = self.rfile.read(content_length)
        except:
            print("receive error invalid request")
            self.send_status_code(404)
            return
        with self.server.kv_lock:
            if self.server.kv.get(scope) is None:
                self.server.kv[scope] = {}
            self.server.kv[scope][key] = value
        self.send_status_code(200)
        _http_server_logger.info(log_str)

    def do_DELETE(self):
        """
        delete method for kv handler, set value according to key.
        """
        log_str = "DELETE " + self.address_string() + self.path
        paths = self.path.split('/')
        if len(paths) < 3:
            print('len of request path must be 3: ' + self.path)
            self.send_status_code(400)
            return
        _, scope, key = paths
        with self.server.delete_kv_lock:
            if self.server.delete_kv.get(scope) is None:
111 112
                self.server.delete_kv[scope] = set()
            self.server.delete_kv[scope].add(key)
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
        self.send_status_code(200)
        _http_server_logger.info(log_str)

    def log_message(self, format, *args):
        """
        ignore all logging messages in kv handler.
        """
        pass

    def send_status_code(self, code):
        """
        send status code back to client.
        """
        self.send_response(code)
        self.send_header("Content-Length", 0)
        self.end_headers()


131
class KVHTTPServer(HTTPServer):
132 133 134 135 136 137
    """
    it is a http server storing kv pairs.
    """

    def __init__(self, port, handler):
        """Init."""
138
        super().__init__(('', port), handler)
139 140 141 142 143 144 145 146 147 148 149
        self.delete_kv_lock = threading.Lock()
        self.delete_kv = {}
        self.kv_lock = threading.Lock()
        self.kv = {}

    def get_deleted_size(self, key):
        """
        get deleted size in key.
        """
        ret = 0
        with self.delete_kv_lock:
150
            ret = len(self.delete_kv.get(key, set()))
151 152 153 154 155 156 157 158 159 160 161 162
        return ret


class KVServer:
    """
    it is a server storing kv pairs, has a http server inside.
    """

    def __init__(self, port, size={}):
        """Init."""
        self.http_server = KVHTTPServer(port, KVHandler)
        self.listen_thread = None
163
        self.size = size
164 165 166 167 168 169

    def start(self):
        """
        start server until user calls stop to let it quit.
        """
        self.listen_thread = threading.Thread(
170 171
            target=lambda: self.http_server.serve_forever()
        )
172 173 174 175 176 177 178 179 180 181
        self.listen_thread.start()

    def stop(self):
        """
        stop server and clear its resources.
        """
        self.http_server.shutdown()
        self.listen_thread.join()
        self.http_server.server_close()

182
    def should_stop(self):
183 184 185 186 187 188 189 190 191 192 193
        """
        return whether the server should stop.

        Returns:
            ret(bool): whether the server should stop
        """
        for key in self.size:
            s = self.http_server.get_deleted_size(key)
            if s != self.size.get(key, 0):
                return False
        return True