Skip to content

Instantly share code, notes, and snippets.

@adontz adontz/epoll-test.py Secret
Created Feb 22, 2019

Embed
What would you like to do?
#!/usr/bin/env python3
from collections import namedtuple
from datetime import datetime
from httptools import HttpRequestParser
from jinja2 import Environment
from jinja2 import FileSystemLoader
from os.path import dirname
import select
import socket
HEAD_TEMPLATE = b'''HTTP/1.1 200 OK
Date: Mon, 13 Jan 2019 12:28:53 GMT
Server: Apache/2.2.14 (Win32)
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
Content-Length: %d
Content-Type: text/html
client_socket: Closed
'''
environment = Environment(auto_reload=False, enable_async=True, loader=FileSystemLoader(dirname(__file__)))
class RequestParserCallback:
def __init__(self):
self.url = b''
self.headers = {}
self.are_headers_complete = False
self.body = b''
self.is_body_complete = False
def on_message_begin(self):
pass
def on_url(self, url: bytes):
self.url = url
def on_header(self, name: bytes, value: bytes):
self.headers[name] = value
def on_headers_complete(self):
self.are_headers_complete = True
def on_body(self, body: bytes):
self.body += body
def on_message_complete(self):
self.is_body_complete = True
State = namedtuple('State', ['socket', 'address', 'parser', 'parser_callback', 'response'])
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
server_socket.bind(('0.0.0.0', 7777))
server_socket.listen(1)
server_socket.setblocking(0)
epoll = select.epoll()
epoll.register(server_socket.fileno(), select.EPOLLIN | select.EPOLLET)
max_fileno = 0
files_watched = 1
max_files_watched = 1
try:
# Use fixed size list to make state access time O(1)
states = [None for i in range(256)]
while True:
events = epoll.poll(1)
for fileno, event in events:
if max_fileno < fileno:
max_fileno = fileno
print('max_fileno =', max_fileno)
if max_files_watched < files_watched:
max_files_watched = files_watched
print('max_files_watched =', max_files_watched)
if fileno == server_socket.fileno():
try:
while True:
client_socket, address = server_socket.accept()
client_socket.setblocking(0)
epoll.register(client_socket.fileno(), select.EPOLLIN | select.EPOLLET)
files_watched += 1
callback = RequestParserCallback()
parser = HttpRequestParser(callback)
states[client_socket.fileno()] = State(client_socket, address, parser, callback, b'')
except socket.error:
pass
elif event & select.EPOLLIN:
try:
while True:
data = states[fileno].socket.recv(1024)
if len(data) == 0:
break
states[fileno].parser.feed_data(data)
except socket.error:
pass
if states[fileno].parser_callback.is_body_complete:
template = environment.get_template('test.html')
body_text = template.render({'system': 'poll....', 'address': states[fileno].address, 'datetime': datetime.now()})
body_data = body_text.encode('utf-8')
states[fileno] = states[fileno]._replace(response=(HEAD_TEMPLATE % len(body_data)) + body_data)
epoll.modify(fileno, select.EPOLLOUT | select.EPOLLET)
elif event & select.EPOLLOUT:
try:
while len(states[fileno].response) > 0:
byteswritten = states[fileno].socket.send(states[fileno].response)
states[fileno] = states[fileno]._replace(response=states[fileno].response[byteswritten:])
except socket.error:
pass
if len(states[fileno].response) == 0:
epoll.modify(fileno, select.EPOLLET)
try:
states[fileno].socket.shutdown(socket.SHUT_RDWR)
except:
pass
elif event & select.EPOLLHUP:
epoll.unregister(fileno)
files_watched -= 1
states[fileno].socket.close()
states[fileno] = None
finally:
epoll.unregister(server_socket.fileno())
epoll.close()
server_socket.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.