def event_schedule(fd, timeout, user_callback): byref_timev = None if timeout is not None: timev = libevent.timeval() timev.tv_sec = int(timeout) timev.tv_usec = int((timeout - int(timeout)) * 1000000) byref_timev = ctypes.byref(timev) event = libevent.event() byref_event = ctypes.byref(event) event_key = utils.set_userdata( (user_callback, byref_event, byref_timev) ) libevent.event_set(byref_event, fd, libevent.EV_READ, event_callback_ptr, event_key) libevent.evtimer_add(byref_event, byref_timev) return lambda : utils.get_and_del_userdata(userdata_key)
def main_loop( bindings ): global base https = [] for (host, port, application) in bindings: log.info("Binding to %s:%i" % (host,port)) http = libevent.evhttp_start(host, port) if not http: log.critical("Bad host/port (host=%r port=%r) or address already in use" % (host,port)) log.critical("QUITTING!") os.abort() libevent.evhttp_set_timeout(http,60) # minute, that's just for the http stuff vhostdata = { 'application':application, 'counter':0, 'host':host, 'port':port, 'requests': {}, 'broken': 0, 'cpu_time':0.0, } libevent.evhttp_set_gencb(http, new_request_callback_ptr, utils.set_userdata(vhostdata)); vhosts.append(vhostdata) https.append(http) # The main libevent loop... Forever. libevent.event_dispatch() # end loop - id est Ctrl+c log.info("Quitting") for http in https: libevent.evhttp_free(http) libevent.event_base_free(base) utils.clear_ref() gc.collect() return returnvalue
def schedule_fd(self, evreq, fd, timeout, event_flag): if getattr(fd, 'fileno', None): fd = fd.fileno() assert(isinstance(fd, int) or isinstance(fd, long)) assert(fd >= 0) self.last_fd = fd byref_timev = None if timeout is not None: timev = libevent.timeval() timev.tv_sec = int(timeout) timev.tv_usec = int((timeout - int(timeout)) * 1000000) byref_timev = ctypes.byref(timev) event = libevent.event() byref_event = ctypes.byref(event) # exit points: event (cleared in handler), connection broken (cleared in tail) self.event_key = utils.set_userdata( (evreq, self, byref_event) ) libevent.event_set(byref_event, fd, event_flag, server.event_callback_ptr, self.event_key) libevent.evtimer_add(byref_event, byref_timev) self.suspended = True return ''
def root_handler(evreq, req, event_type, evt=None): content_length = 0 chunks_number = 0 response_dict = {'code':0, 'reason':''} request_closed = False # dispatcher, new reqest or resume one? # head. if event_type == REQUEST_NEW: response_dict, chunked, iterable = req.start_wsgi_application() if not chunked: data = '' for data in iterable: break data = str(data) buf = libevent.evbuffer_new() for key, value in response_dict['headers']: libevent.evhttp_add_header(evreq.contents.output_headers, str(key), str(value)) libevent.evbuffer_add(buf, data, len(data)) libevent.evhttp_send_reply(evreq, response_dict['code'], response_dict['reason'], buf) libevent.evbuffer_free(buf) response_dict['transmitted'] = True req.content_length += len(data) req.chunks_number += 1 content_length = len(data) chunks_number = 1 del buf if event_type == REQUEST_CONTINUE: response_dict, chunked, iterable = req.continue_wsgi_application(True if evt and (evt & libevent.EV_TIMEOUT) else False) assert(chunked) # body. if event_type in [REQUEST_NEW, REQUEST_CONTINUE] and chunked: assert(getattr(iterable, '__iter__', None)) iterator = getattr(iterable, '__iter__')() try: first_chunk = str(iterator.next()) except StopIteration: first_chunk = None if not req.buf: # must be after first evaluation of iterable req.buf = libevent.evbuffer_new() for key, value in response_dict['headers']: if req.environ['SERVER_PROTOCOL'] == 'HTTP/1.1' and key == "Transfer-Encoding": # libevent automagically adds it's own transer-encoding header. f**k you libevent continue libevent.evhttp_add_header(evreq.contents.output_headers, str(key), str(value)) libevent.evhttp_send_reply_start(evreq, response_dict['code'], response_dict['reason']) response_dict['transmitted'] = True # exit points: connection broken (cleared in handler), end of request (cleared in tail) req.close_key = utils.set_userdata( (evreq, req) ) libevent.evhttp_connection_set_closecb(evreq.contents.evcon, close_callback_ptr, req.close_key) if first_chunk: libevent.evbuffer_add(req.buf, first_chunk, len(first_chunk)) libevent.evhttp_send_reply_chunk(evreq, req.buf) req.content_length += len(first_chunk) req.chunks_number += 1 content_length += len(first_chunk) chunks_number += 1 # now transfer all the chunks for data_chunk in iterator: data_chunk = str(data_chunk) if not data_chunk: # can't really send empty data -> it means conn close continue # TODO: bug here, packets aren't on the wire just after evhttp_send_reply_chunk, and they should # TODO: this is not nice, but this is how http works in libevent, sorry. libevent.evbuffer_add(req.buf, data_chunk, len(data_chunk)) libevent.evhttp_send_reply_chunk(evreq, req.buf) req.content_length += len(data_chunk) req.chunks_number += 1 content_length += len(data_chunk) chunks_number += 1 # before closing req.update_cpu_time() # tail. if event_type == REQUEST_CLOSE or (chunked and req.is_closed()): if evreq.contents.evcon and evreq.contents.evcon.contents and evreq.contents.evcon.contents.closecb: libevent.evhttp_connection_set_closecb(evreq.contents.evcon, void_callback_ptr, None) if req.close_key: assert(event_type != REQUEST_CLOSE) utils.get_and_del_userdata(req.close_key) del req.close_key if event_type != REQUEST_CLOSE: libevent.evhttp_send_reply_end(evreq) else: assert(event_type == REQUEST_CLOSE) if evreq.contents.evcon: # BUG in libevent, memleak if conn is broken ''' event = libevent.event() timeval = libevent.timeval() timeval.tv_sec = 0 timeval.tv_usec = 1 byref_event = ctypes.byref(event) byref_timev = ctypes.byref(timeval) libevent.evtimer_set(byref_event, freeevcon_callback_ptr, utils.set_userdata( (evreq.contents.evcon, byref_event, byref_timev) ) ) libevent.evtimer_add(byref_event, byref_timev) ''' pass if req.buf: libevent.evbuffer_free(req.buf) del req.buf # free event, if scheduled: if req.event_key: assert(event_type == REQUEST_CLOSE) _, _, byref_event = utils.get_and_del_userdata( req.event_key ) libevent.event_del(byref_event) del byref_event del req.event_key req.close(con_broken=True if event_type == REQUEST_CLOSE else False) request_closed = True if event_type == REQUEST_NEW and not chunked:# cleanup single req.close(con_broken=False) # How the f**k to force closing connection here..., after sendig data #libevent.evhttp_request_free(evreq) #libevent.evhttp_connection_free(evreq.contents.evcon) log.debug('''%(host)s "%(method)s %(url)s %(http)s" %(status_code)i %(content_length)s/%(chunks_number)s (%(time).1fms) - %(event)s''' % { 'method': req.environ['REQUEST_METHOD'], 'url': req.get_url(), 'http': req.environ['SERVER_PROTOCOL'], 'status_code': response_dict['code'], 'content_length': content_length, 'chunks_number': chunks_number, 'host': '%s:%i' % (req.environ['REMOTE_HOST'], req.environ['REMOTE_PORT']), 'time': (req.now_cpu_time) * 1000, # in miliseconds 'event': 'close' if request_closed else 'single' if not chunked else 'chunk', }) return 1