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;
}