Beispiel #1
0
def event_callback(fd, evt, userdata_key):
    global now, event_counter
    now = time.time()
    event_counter += 1
    evreq, req, byref_event = utils.get_and_del_userdata(userdata_key)
    req.event_key = None

    return root_handler(evreq, req, REQUEST_CONTINUE, evt)
Beispiel #2
0
def close_callback(conn, userdata_key):
    global now, event_counter
    now = time.time()
    event_counter += 1

    evreq, req = utils.get_and_del_userdata(userdata_key)
    req.close_key = None

    return root_handler(evreq, req, REQUEST_CLOSE)
Beispiel #3
0
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)
Beispiel #4
0
def event_callback(fd, evt, userdata_key):
    user_callback, _, _ = utils.get_and_del_userdata(userdata_key)
    user_callback(True if evt and (evt & libevent.EV_TIMEOUT) else False)
    return 0
Beispiel #5
0
def freeevcon_callback(fd, evt, userdata_key):
    evcon, byref_event, byref_timev = utils.get_and_del_userdata(userdata_key)
    del byref_event
    del byref_timev
    libevent.evhttp_connection_free(evcon)
    return 0
Beispiel #6
0
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