python-3.xserverpython-requestshttp-postbasehttprequesthandler

fill and return list in BaseHTTPRequestHandler


I cannot return list in BaseHTTPRequestHandler class. Here is my code with comments Idea is to forward incoming post request with json to another function, handle it by filling new list/dict and return it. I cannot also understand, whether such object cannot be returned via requests.post at all

from http.server import BaseHTTPRequestHandler, HTTPServer  
import os  
import time
import threading    
import requests
import json
import calendar

    def calculate(Event_dictionary_list, cached_timestamp):
        Event_dictionary_list.clear() #- empty Event_dictionary_list
        cached_timestamp = 0   
    def Async_func(Event_dictionary_list, cached_timestamp):
        calculate(Event_dictionary_list, cached_timestamp)            
    def handler(event, Event_dictionary_list, cached_timestamp):
        if not Event_dictionary_list: #Checking if Event_dictionary_list is empty 
            cached_timestamp = event['time']
            threading.Timer(60,Async_func, [Event_dictionary_list, cached_timestamp]).start()
            # make decision as to when to calculate
        if event['time'] - cached_timestamp < 60*1000: #- in milliseconds
            Event_dictionary_list.append(event)
    
    class server(BaseHTTPRequestHandler):
        
        def __init__(self, *args, **kwargs):
            self.Event_dictionary_list = []
            self.cached_timestamp = 0
            super().__init__(*args, **kwargs)
                
        def do_POST(self):
            print("post msg received");
            self.data_string = self.rfile.read(int(self.headers['Content-Length']))
            self.event_time = calendar.timegm(time.gmtime())
            self.send_response(200) # 200 = success - thanks for message response
            self.send_header("Content-type", "application/json")
            self.end_headers()
            data = json.loads(self.data_string)
            data['time'] = self.event_time
            # we assume json will be decoded as object, eg:
            if type(data) is dict:
                    handler(data, self.Event_dictionary_list, self.cached_timestamp)
            #Trying to return object, but no luck
            return self.Event_dictionary_list 
            
    def run():
        print('http server is starting...')
        port = 8000
        server_address = ('localhost', port)  
        httpd = HTTPServer(server_address, server)  
        print('http server is listening on port %d' % port)  
        httpd.serve_forever()    
    if __name__ == '__main__':  
      run()

My request looks like this:

requests.post(url = 'http://localhost:8000', json = {'key': 'value'})

Solution

  • First: you need to return some string data - not Python object such as list, dict etc. You can encode the python object to Json string for example and send this string.

    Second: I recommend to subclass HTTPServer and put the "persistent" data there.

    Example:

    import json
    from http.server import BaseHTTPRequestHandler, HTTPServer
    
    
    class MyServer(HTTPServer):
        def __init__(self, *args, **kwargs):
            HTTPServer.__init__(self, *args, **kwargs)
            self.my_list = []
    
    
    class MyCustomHandler(BaseHTTPRequestHandler):
        def do_POST(self):
            # read the data in chunks:
            max_chunk_size = 10 * 1024 * 1024
            size_remaining = int(self.headers["content-length"])
            L = []
            while size_remaining:
                chunk_size = min(size_remaining, max_chunk_size)
                chunk = self.rfile.read(chunk_size)
    
                if not chunk:
                    break
    
                L.append(chunk)
                size_remaining -= len(L[-1])
    
            try:
                data = json.loads(b"".join(L).decode("utf-8"))
            except:
                self.send_response(500)
                self.send_header("Content-length", "0")
                self.end_headers()
            else:
                self.server.my_list.append(data)
    
                response = json.dumps(self.server.my_list, indent=4).encode("utf-8")
    
                self.send_response(200)
                self.send_header("Content-type", "application/json")
    
                self.send_header("Content-length", str(len(response)))
                self.end_headers()
                self.wfile.write(response)
    
    
    def run():
        print("http server is starting...")
        httpd = MyServer(("localhost", 8000), MyCustomHandler)
        print("http server is listening on port %d" % port)
        httpd.serve_forever()
    
    
    if __name__ == "__main__":
        run()
    

    Running the code starts listening server on localhost:8000.

    Then in other terminal you can do:

    $ curl -X POST -H "Content-Type: application/json" -d '{"key": "value"}' http://localhost:8000
    
    [
        {
            "key": "value"
        }
    ]
    

    Doing subsequent curl commands will add to the list on the server side and return it.