Jeff, Yes, I do realize that it is not obvious how it works, especially without reading the papers :-) I will try to clear it up a bit.. but you will need the source code of NetAirt, as I am going to refer to it below. (http://www.globule.org/netairt/netairt-2.0.46-0.2-thin.tgz)
> I'm at a loss as to what the big picture is with the programming model > enabled by this path. I could go read the whole "DNS+HTTP redirection > system" but I'd rather not :) :-) I guess there is not too much reading anyway: you can find technical details in the master's thesis -- just skip all the introductory stuff. The paper version only gives a general picture, no technical details whatsoever. > How does process_func field ever get set? What type of MPM thread would > be used to process the UDP -- the type of thread that polls on sockets > or the type of thread that processes work. It doesn't fit well within > the rest of Apache to assume that the "accept"-ing thread should process > the message. OK, here is how it goes. Listeners are created in configuration directive callbacks. This is where you append listeners to the global list, and this is where you set the fields, both accept_func, and process_func (see mod_netairt.c::dns_config_mode()). UDP listeners are marked as 'configured' to avoid Apache messing with them, yet at this stage there are just unconfigured sockets. Since allocation is done in the module, I need old_listeners to be visible to re-use sockets (see dns_comm.c::dns_comm_alloc_listener(), which is somehow very similar to the original Apache's alloc_listener() ;-)). After allocating, listeners survive until the end of configuration. In the post_config hook, I call dns_config::dns_config_init() to configure the UDP listeners (setsockopt's and binding). What we have now, is a working UDP listener with two custom functions, 'accept' and 'process'. Since accept's are serialized, we don't want to do any long-lasting stuff there. So, inside 'accept', we only retrieve all immediately-available datagrams, create a fake TCP socket to be returned (to fool some MPMs, such as worker, which tries to close the socket in some special cases), and register the listener inside the transaction pool, together with a list of the just-received datagrams (see dns_comm.c::dns_comm_accept_udp()). The fake TCP socket and the transaction pool (with our stuff hidden inside) is passed to the connection-processing part. This is where things go wild. I patched the Apache connection-processing function to check for the hidden UDP listeners before creating the connection record. If found, the customized 'process' function is called, and the connection processing is silently aborted afterwards. The 'process' function destroys the fake TCP socket, digs up the hidden datagram list and processes them (see dns_comm.c::dns_comm_process_udp()). It happens in the thread that called the connection-processing function, so I think that it is the right one. At least, it works fine for both prefork and worker. After datagrams are processed, and responses sent (using the same UDP socket, with a mutex for correctness), the UDP listener and the datagram list are unregistered from the transaction pool, as these pools are usually reused. Voila. > Why does ap_old_listeners need to be externalized? For reusing by the module-private listener allocator (see above). > About this code below: It seems like process_func is a flag that means > ????? (I dunno; why wouldn't we be using the existing record here). Is > that inherently connected to having a process_func, or are those > separate issues? > > /* Some listeners are not real so they will not have a > bind_addr. */ > if (sa) { > apr_sockaddr_port_get(&oldport, sa); > ! if (!strcmp(sa->hostname, addr) && port == oldport && > !(*walk)->process_func) { > /* re-use existing record */ > new = *walk; > *walk = new->next; When writing NetAirt, I assumed that there will be only my UDP listeners inside, all using 'process' functions. Since you can have 2 listeners operating for the same host:port pair (1 TCP, and 1 UDP), I had to distinguish them somehow, so I used the process_function nobody else could use. I agree that it is not very clean solution -- it simply assumes that process_func are only defined for UDP sockets. > Regarding the code below: Setting a key in a pool is not an appropriate > way to get Apache to do some special processing on a socket. Surely the > problem is that Apache doesn't have quite the right hook currently and > an existing hook needs to be modified in Apache 2.1-dev or some new hook > is needed? > > { > apr_status_t rv; > ! conn_rec *c; > ! ap_listen_rec * lr = NULL; > ! > ! rv = apr_pool_userdata_get((void**)&lr,AP_LISTEN_REC_KEY,ptrans); > ! if (rv == APR_SUCCESS && lr && lr->process_func) { > ! apr_pool_userdata_set(NULL,AP_LISTEN_REC_KEY,NULL,ptrans); > ! lr->process_func(csd,lr,server,ptrans); > ! return NULL; > ! } Of course, adding a new hook at this stage would be a much more clean solution. Somehow I could not figure out how to do that myself, so I came up with hacking the connection-processing function. And if you implement it as a new hook, could you also think about a better way of passing data from 'accept' to that new hook? You are right that using pools as bags is not the most elegant thing to do :-) All this shows that running UDP in Apache can make sense, and I tried to get some attention about it one year ago.. But somehow everybody thought that UDP inside Apache can be used only for HTTP-over-UDP, which is indeed controversial.. I hope that full UDP-support will not be neglected in the next Apache release :-) Regards, M. -- Michal Szymaniak | mailto:[EMAIL PROTECTED] | http://www.cs.vu.nl/~mszyman