code: async def handle_client_request(self, req, res):
          
          # create a middleware generator
          mw_generator = self.middleware(req.method, req.path)
  
          # loop through middleware
          for mw in mw_generator:
  
              # try calling the function
              try:
                  ret_val = mw(req, res)
                  if inspect.isawaitable(ret_val):
                      await ret_val
  
              # special exception - immediately stop the loop
              #  - do not check if res has sent
              except GrowlerStopIteration:
                  return None
  
              # on an unhandled exception - notify the generator of the error
              except Exception as error:
                  mw_generator.throw(error)
                  await self.handle_server_error(req, res, mw_generator, error)
                  return
  
              if res.has_ended:
                  break
  
          if not res.has_ended:
              self.handle_response_not_sent(req, res)
docs: Entry point for the request + response middleware chain.
          This is called by growler.HTTPResponder (the default responder)
          after the headers have been processed in the begin_application
          method.
  
          This iterates over all middleware in the middleware list which
          matches the client's method and path.
          It executes the middleware and continues iterating until the
          res.has_ended property is true.
  
          If the middleware raises a GrowlerStopIteration exception, this
          method immediately returns None, breaking the loop and leaving
          res without sending any information back to the client. Be *sure*
          that you have another coroutine scheduled that will take over
          handling client data.
  
          If a middleware function raises any other exception, the
          exception is forwarded to the middleware generator, which changes
          behavior to generating any error handlers it had encountered.
          This method then calls the handle_server_error method which
          *should* handle the error and notify the user.
  
          If after the chain is exhausted, either with an exception raised
          or not, res.has_ended does not evaluate to true, the response
          is sent a simple server error message in text.
  
          Args:
              req (growler.HTTPRequest): The incoming request, containing
                  all information about the client.
              res (growler.HTTPResponse): The outgoing response, containing
                  methods for sending headers and data back to the client.