Given all the input on this thread, I arrive at the following pseudo code:

1. Post Read Request Hook:
   
        if (Upgrade: request header present) {
                collect protocol proposals;
                ps = protocol with highest preference from proposals;
                if (ps && ps != current) {
                        status = switch protocol(phase => post_read);
                        if (status == APR_EOF) {
                                close connection;
                        }
                        else if (status == APR_EAGAIN) {
                                // protocol switch wants to be called later 
before handler
                                if (request is "OPTIONS *") {
                                        // TODO: invoke again with (phase => 
handler)?
                                }
                                request->upgrade_protocol = ps;
                        }
                        else {
                                // continue, HTTP/1 processing. May happen on 
TLS Upgrade
                                // or if the protocol managed to switch back to 
HTTP/1
                                // process other stati than APR_SUCCESS as usual
                        }
                }
                else if ("http/1.1" not in Protocols) {
                        answer "505 Not Supported";
                }
        }
        else if ("http/1.1" not in Protocols) {
                answer "426 Upgrade Required";
        }

2. Handler Hook (First or just before):

        if (r->upgrade_protocol) {
                status = switch protocol(phase => handler);
                if (status == APR_EOF) {
                        close connection;
                }
                else {
                        // continue, HTTP/1 processing. May happen on TLS 
Upgrade
                        // or if the protocol managed to switch back to HTTP/1
                        // process other stati than APR_SUCCESS as usual
                }
        }

3. Common protocol switch hook behavior:

        apr_status_t process_body(apr_bucket_brigade *bb, void *ctx)
        {
                // brigade contains unchunked body data of request
                // may be called several times. EOS bucket in brigade
                // indicates end of body.
                // If request has no body, will be called once with EOS bucket.
        }

        int xxx_switch(conn_rec *c, request_rec *r, server_rec *s, 
                const char *protocol, int phase)
        {
                apr_status_t stats;

                if (strcmp(my_protocol, protocol)) {
                        return DECLINED;
                }
                
                if (need_handler && phase != handler) {
                        return APR_EAGAIN;
                }

                status = ap_upgrade_request(r, protocol, phase, 
my_additional_header, process_body);
                if (status != APR_SUCCESS) {
                        // any type of error, could also fail if NULL is passed 
instead of
                        // &process_body and request has body
                        return status;
                }

                // c->output_filters can be written
                // c->input_filters can be read, will pass data to 
process_body() until body is done,
                // then return data up the filter chain.
                process protocol;

                return APR_EOF;
        }

Reply via email to