Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-40700: make WSGIRequestHandler can use an easy way to replace the ServerHandler #20262

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

Zheaoli
Copy link
Contributor

@Zheaoli Zheaoli commented May 20, 2020

Copy link
Member

@FFY00 FFY00 left a comment

I think we could use some checks to make sure server_handler is valid, instead of just giving somewhat obscure runtime errors when it is not.

Having a test would also be great :)

Copy link
Contributor

@remilapeyre remilapeyre left a comment

That seems a good idea, can you add some tests and update the documentation for the new attribute?

server_handler = ServerHandler

def __init__(self, *args, **kwargs):
if not issubclass(self.server_handler, BaseHandler):
Copy link
Contributor

@remilapeyre remilapeyre May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this check is appropriate, if someone duck-types BaseHandler, the RequestHandler should accept it restricting the user.

Copy link
Member

@FFY00 FFY00 May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would someone duck-type BaseHandler instead of just subclassing it. If there's a good reason then that is fine, otherwise we are just supporting calling this in yet another way, and that way requires us to implement a more complex verification routine.

Copy link
Contributor Author

@Zheaoli Zheaoli May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@remilapeyre I agree with @FFY00, we should make it as simple as we can

Copy link
Member

@zhangyangyu zhangyangyu Jun 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think check a class variable inside instance constructor is a good idea. I think it's fine to omit it and use server_handler directly. Those who want to extend should ensure it's of correct type.

@@ -347,6 +347,14 @@ request. (E.g., using the :func:`shift_path_info` function from
:func:`make_server` function. Some possibly relevant methods for overriding in
subclasses:

:class:`WSGIRequestHandler` has the following instance variables:
Copy link
Member

@FFY00 FFY00 May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a class variable, not instance variable. I wonder if the implementation would not be better just making it an instance variable and passing the argument in the constructor.

Copy link
Contributor Author

@Zheaoli Zheaoli May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the BaseHandler initial process is dependent on some special variable what is existed in the request. So I don't think to make it as an instance variable is a good idea

Copy link
Contributor Author

@Zheaoli Zheaoli May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix the docs

Copy link
Member

@FFY00 FFY00 May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still, I don't see the problem on setting server_handler in the constructor. Am I missing some context here?

If you make it a class attribute, if you only want to change the server handler you still need to subclass WSGIRequestHandler.

Copy link
Contributor Author

@Zheaoli Zheaoli May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        handler = self.server_handler(
            self.rfile, self.wfile, self.get_stderr(), self.get_environ(),
            multithread=False,
        )

the BaseHandler initial process needs the request environment, here's the detail

    def get_environ(self):
        env = self.server.base_environ.copy()
        env['SERVER_PROTOCOL'] = self.request_version
        env['SERVER_SOFTWARE'] = self.server_version
        env['REQUEST_METHOD'] = self.command
        if '?' in self.path:
            path,query = self.path.split('?',1)
        else:
            path,query = self.path,''

        env['PATH_INFO'] = urllib.parse.unquote(path, 'iso-8859-1')
        env['QUERY_STRING'] = query

        host = self.address_string()
        if host != self.client_address[0]:
            env['REMOTE_HOST'] = host
        env['REMOTE_ADDR'] = self.client_address[0]

        if self.headers.get('content-type') is None:
            env['CONTENT_TYPE'] = self.headers.get_content_type()
        else:
            env['CONTENT_TYPE'] = self.headers['content-type']

        length = self.headers.get('content-length')
        if length:
            env['CONTENT_LENGTH'] = length

        for k, v in self.headers.items():
            k=k.replace('-','_').upper(); v=v.strip()
            if k in env:
                continue                    # skip content length, type,etc.
            if 'HTTP_'+k in env:
                env['HTTP_'+k] += ','+v     # comma-separate multiple headers
            else:
                env['HTTP_'+k] = v
        return env

Some of the environment message is only existed in the HTTP request, that means that we need make an instance of BaseHandler for every request income

Copy link
Member

@FFY00 FFY00 May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I still fail to see a reason why it wouldn't be better to just have this:

class WSGIRequestHandler(BaseHTTPRequestHandler):
    ...
    def __init__(self, server_handler=ServerHandler, *args, **kwargs):
        self.server_handler = server_handler
        ...

get_environ is an instance method so it never gets called in a context where self.server_handler would not be set.

Copy link
Contributor Author

@Zheaoli Zheaoli May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I think I get your means, yes, this would be a good idea

Copy link
Contributor Author

@Zheaoli Zheaoli May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FFY00 Oh No, it doesn't work actually. I have double-checked the code, here's the detail

The WSGIRequestHandler initial process is not controlled by the user, by the server_class

def make_server(
    host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler
):
    """Create a new WSGI server listening on `host` and `port` for `app`"""
    server = server_class((host, port), handler_class)
    server.set_app(app)
    return server

The user always uses the ReqeustHandler as a param when they try to use the make_server, and let the server_class to control it when it can be made as an instance. So, if we change the constructor, I think it will make a big and broken change for the make_server function and server_class

Copy link
Member

@FFY00 FFY00 May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense 😄

Copy link
Contributor Author

@Zheaoli Zheaoli May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks bro/mses, this discussion is very useful for
me!😀😀

.. versionadded:: 3.9

The handler class to handle the request. This value should be the subclass
:class:`wsgiref.handlers.BaseHandler`
Copy link
Member

@zhangyangyu zhangyangyu Jun 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know we should expose this attribute or not. For me, the handle() method has already document the behaviour very well.

@cpython-cla-bot
Copy link

cpython-cla-bot bot commented Apr 29, 2022

The following commit authors need to sign the Contributor License Agreement:

Click the button to sign:
CLA not signed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants