Rocket Server 启动一个线程监听客户端的连接,收到连接将连接放置到队列中。线程池中的Worker会以这个连接进行初始化。Rocket中Worker的基类是:

class Worker(Thread):
"""The Worker class is a base class responsible for receiving connections
and (a subclass) will run an application to process the the connection """ def __init__(self,
app_info,
active_queue,
monitor_queue,
*args,
**kwargs): Thread.__init__(self, *args, **kwargs) # Instance Variables
self.app_info = app_info
self.active_queue = active_queue
self.monitor_queue = monitor_queue self.size = 0
self.status = "200 OK"
self.closeConnection = True
self.request_line = ""
self.protocol = 'HTTP/1.1' # Request Log
self.req_log = logging.getLogger('Rocket.Requests')
self.req_log.addHandler(NullHandler()) # Error Log
self.err_log = logging.getLogger('Rocket.Errors.' + self.getName())
self.err_log.addHandler(NullHandler()) def _handleError(self, typ, val, tb):
if typ == SSLError:
if 'timed out' in str(val.args[0]):
typ = SocketTimeout
if typ == SocketTimeout:
if __debug__:
self.err_log.debug('Socket timed out')
self.monitor_queue.put(self.conn)
return True
if typ == SocketClosed:
self.closeConnection = True
if __debug__:
self.err_log.debug('Client closed socket')
return False
if typ == BadRequest:
self.closeConnection = True
if __debug__:
self.err_log.debug('Client sent a bad request')
return True
if typ == socket.error:
self.closeConnection = True
if val.args[0] in IGNORE_ERRORS_ON_CLOSE:
if __debug__:
self.err_log.debug('Ignorable socket Error received...'
'closing connection.')
return False
else:
self.status = "999 Utter Server Failure"
tb_fmt = traceback.format_exception(typ, val, tb)
self.err_log.error('Unhandled Error when serving '
'connection:\n' + '\n'.join(tb_fmt))
return False self.closeConnection = True
tb_fmt = traceback.format_exception(typ, val, tb)
self.err_log.error('\n'.join(tb_fmt))
self.send_response('500 Server Error')
return False def run(self):
if __debug__:
self.err_log.debug('Entering main loop.') # Enter thread main loop
while True:
conn = self.active_queue.get() if not conn:
# A non-client is a signal to die
if __debug__:
self.err_log.debug('Received a death threat.')
return conn if isinstance(conn, tuple):
conn = Connection(*conn) self.conn = conn if conn.ssl != conn.secure:
self.err_log.info('Received HTTP connection on HTTPS port.')
self.send_response('400 Bad Request')
self.closeConnection = True
conn.close()
continue
else:
if __debug__:
self.err_log.debug('Received a connection.')
self.closeConnection = False # Enter connection serve loop
while True:
if __debug__:
self.err_log.debug('Serving a request')
try:
self.run_app(conn)
except:
exc = sys.exc_info()
handled = self._handleError(*exc)
if handled:
break
finally:
if self.request_line:
log_info = dict(client_ip=conn.client_addr,
time=datetime.now().strftime('%c'),
status=self.status.split(' ')[0],
size=self.size,
request_line=self.request_line)
self.req_log.info(LOG_LINE % log_info) if self.closeConnection:
try:
conn.close()
except:
self.err_log.error(str(traceback.format_exc())) break def run_app(self, conn):
# Must be overridden with a method reads the request from the socket
# and sends a response.
self.closeConnection = True
raise NotImplementedError('Overload this method!') def send_response(self, status):
stat_msg = status.split(' ', 1)[1]
msg = RESPONSE % (self.protocol,
status,
len(stat_msg),
'text/plain',
stat_msg)
try:
self.conn.sendall(b(msg))
except socket.timeout:
self.closeConnection = True
msg = 'Tried to send "%s" to client but received timeout error'
self.err_log.error(msg % status)
except socket.error:
self.closeConnection = True
msg = 'Tried to send "%s" to client but received socket error'
self.err_log.error(msg % status) def read_request_line(self, sock_file):
self.request_line = ''
try:
# Grab the request line
d = sock_file.readline()
if PY3K:
d = d.decode('ISO-8859-1') if d == '\r\n':
# Allow an extra NEWLINE at the beginning per HTTP 1.1 spec
if __debug__:
self.err_log.debug('Client sent newline') d = sock_file.readline()
if PY3K:
d = d.decode('ISO-8859-1')
except socket.timeout:
raise SocketTimeout('Socket timed out before request.')
except TypeError:
raise SocketClosed(
'SSL bug caused closure of socket. See '
'"https://groups.google.com/d/topic/web2py/P_Gw0JxWzCs".') d = d.strip() if not d:
if __debug__:
self.err_log.debug(
'Client did not send a recognizable request.')
raise SocketClosed('Client closed socket.') self.request_line = d # NOTE: I've replaced the traditional method of procedurally breaking
# apart the request line with a (rather unsightly) regular expression.
# However, Java's regexp support sucks so bad that it actually takes
# longer in Jython to process the regexp than procedurally. So I've
# left the old code here for Jython's sake...for now.
if IS_JYTHON:
return self._read_request_line_jython(d) match = re_REQUEST_LINE.match(d) if not match:
self.send_response('400 Bad Request')
raise BadRequest req = match.groupdict()
for k, v in req.iteritems():
if not v:
req[k] = ""
if k == 'path':
req['path'] = r'%2F'.join(
[unquote(x) for x in re_SLASH.split(v)]) self.protocol = req['protocol']
return req def _read_request_line_jython(self, d):
d = d.strip()
try:
method, uri, proto = d.split(' ')
if not proto.startswith('HTTP') or \
proto[-3:] not in ('1.0', '1.1') or \
method not in HTTP_METHODS:
self.send_response('400 Bad Request')
raise BadRequest
except ValueError:
self.send_response('400 Bad Request')
raise BadRequest req = dict(method=method, protocol=proto)
scheme = ''
host = ''
if uri == '*' or uri.startswith('/'):
path = uri
elif '://' in uri:
scheme, rest = uri.split('://')
host, path = rest.split('/', 1)
path = '/' + path
else:
self.send_response('400 Bad Request')
raise BadRequest query_string = ''
if '?' in path:
path, query_string = path.split('?', 1) path = r'%2F'.join([unquote(x) for x in re_SLASH.split(path)]) req.update(path=path,
query_string=query_string,
scheme=scheme.lower(),
host=host)
return req def read_headers(self, sock_file):
try:
headers = dict()
lname = None
lval = None
while True:
l = sock_file.readline() if PY3K:
try:
l = str(l, 'ISO-8859-1')
except UnicodeDecodeError:
self.err_log.warning(
'Client sent invalid header: ' + repr(l)) if l.strip().replace('\0', '') == '':
break if l[0] in ' \t' and lname:
# Some headers take more than one line
lval += ' ' + l.strip()
else:
# HTTP header values are latin-1 encoded
l = l.split(':', 1)
# HTTP header names are us-ascii encoded lname = l[0].strip().upper().replace('-', '_')
lval = l[-1].strip() headers[str(lname)] = str(lval) except socket.timeout:
raise SocketTimeout("Socket timed out before request.") return headers

Worker继承自Thread,线程启动会执行run方法,在这个方法中做了以下的事情:

1、从连接队列中取得con对象

2、执行方法run_app(con),这个方法由子类实现

Rocket中Worker子类是:

class WSGIWorker(Worker):
def __init__(self, *args, **kwargs):
"""Builds some instance variables that will last the life of the
thread."""
Worker.__init__(self, *args, **kwargs) if isinstance(self.app_info, dict):
multithreaded = self.app_info.get('max_threads') != 1
else:
multithreaded = False
self.base_environ = dict(
{'SERVER_SOFTWARE': self.app_info['server_software'],
'wsgi.multithread': multithreaded,
})
self.base_environ.update(BASE_ENV) # Grab our application
self.app = self.app_info.get('wsgi_app') if not hasattr(self.app, "__call__"):
raise TypeError("The wsgi_app specified (%s) is not a valid WSGI application." % repr(self.app)) # Enable futures
if has_futures and self.app_info.get('futures'):
executor = self.app_info['executor']
self.base_environ.update({"wsgiorg.executor": executor,
"wsgiorg.futures": executor.futures}) def build_environ(self, sock_file, conn):
""" Build the execution environment. """
# Grab the request line
request = self.read_request_line(sock_file) # Copy the Base Environment
environ = self.base_environ.copy() # Grab the headers
for k, v in self.read_headers(sock_file).iteritems():
environ[str('HTTP_' + k)] = v # Add CGI Variables
environ['REQUEST_METHOD'] = request['method']
environ['PATH_INFO'] = request['path']
environ['SERVER_PROTOCOL'] = request['protocol']
environ['SERVER_PORT'] = str(conn.server_port)
environ['REMOTE_PORT'] = str(conn.client_port)
environ['REMOTE_ADDR'] = str(conn.client_addr)
environ['QUERY_STRING'] = request['query_string']
if 'HTTP_CONTENT_LENGTH' in environ:
environ['CONTENT_LENGTH'] = environ['HTTP_CONTENT_LENGTH']
if 'HTTP_CONTENT_TYPE' in environ:
environ['CONTENT_TYPE'] = environ['HTTP_CONTENT_TYPE'] # Save the request method for later
self.request_method = environ['REQUEST_METHOD'] # Add Dynamic WSGI Variables
if conn.ssl:
environ['wsgi.url_scheme'] = 'https'
environ['HTTPS'] = 'on'
try:
peercert = conn.socket.getpeercert(binary_form=True)
environ['SSL_CLIENT_RAW_CERT'] = \
peercert and ssl.DER_cert_to_PEM_cert(peercert)
except Exception:
print sys.exc_info()[1]
else:
environ['wsgi.url_scheme'] = 'http' if environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked':
environ['wsgi.input'] = ChunkedReader(sock_file)
else:
environ['wsgi.input'] = sock_file return environ def send_headers(self, data, sections):
h_set = self.header_set # Does the app want us to send output chunked?
self.chunked = h_set.get('Transfer-Encoding', '').lower() == 'chunked' # Add a Date header if it's not there already
if not 'Date' in h_set:
h_set['Date'] = formatdate(usegmt=True) # Add a Server header if it's not there already
if not 'Server' in h_set:
h_set['Server'] = HTTP_SERVER_SOFTWARE if 'Content-Length' in h_set:
self.size = int(h_set['Content-Length'])
else:
s = int(self.status.split(' ')[0])
if (s < 200 or s not in (204, 205, 304)) and not self.chunked:
if sections == 1 or self.protocol != 'HTTP/1.1':
# Add a Content-Length header because it's not there
self.size = len(data)
h_set['Content-Length'] = str(self.size)
else:
# If they sent us more than one section, we blow chunks
h_set['Transfer-Encoding'] = 'Chunked'
self.chunked = True
if __debug__:
self.err_log.debug('Adding header...'
'Transfer-Encoding: Chunked') if 'Connection' not in h_set:
# If the application did not provide a connection header,
# fill it in
client_conn = self.environ.get('HTTP_CONNECTION', '').lower()
if self.environ['SERVER_PROTOCOL'] == 'HTTP/1.1':
# HTTP = 1.1 defaults to keep-alive connections
if client_conn:
h_set['Connection'] = client_conn
else:
h_set['Connection'] = 'keep-alive'
else:
# HTTP < 1.1 supports keep-alive but it's quirky
# so we don't support it
h_set['Connection'] = 'close' # Close our connection if we need to.
self.closeConnection = h_set.get('Connection', '').lower() == 'close' # Build our output headers
header_data = HEADER_RESPONSE % (self.status, str(h_set)) # Send the headers
if __debug__:
self.err_log.debug('Sending Headers: %s' % repr(header_data))
self.conn.sendall(b(header_data))
self.headers_sent = True def write_warning(self, data, sections=None):
self.err_log.warning('WSGI app called write method directly. This is '
'deprecated behavior. Please update your app.')
return self.write(data, sections) def write(self, data, sections=None):
""" Write the data to the output socket. """ if self.error[0]:
self.status = self.error[0]
data = b(self.error[1]) if not self.headers_sent:
self.send_headers(data, sections) if self.request_method != 'HEAD':
try:
if self.chunked:
self.conn.sendall(b('%x\r\n%s\r\n' % (len(data), data)))
else:
self.conn.sendall(data)
except socket.timeout:
self.closeConnection = True
except socket.error:
# But some clients will close the connection before that
# resulting in a socket error.
self.closeConnection = True def start_response(self, status, response_headers, exc_info=None):
""" Store the HTTP status and headers to be sent when self.write is
called. """
if exc_info:
try:
if self.headers_sent:
# Re-raise original exception if headers sent
# because this violates WSGI specification.
raise
finally:
exc_info = None
elif self.header_set:
raise AssertionError("Headers already set!") if PY3K and not isinstance(status, str):
self.status = str(status, 'ISO-8859-1')
else:
self.status = status
# Make sure headers are bytes objects
try:
self.header_set = Headers(response_headers)
except UnicodeDecodeError:
self.error = ('500 Internal Server Error',
'HTTP Headers should be bytes')
self.err_log.error('Received HTTP Headers from client that contain'
' invalid characters for Latin-1 encoding.') return self.write_warning def run_app(self, conn):
self.size = 0
self.header_set = Headers([])
self.headers_sent = False
self.error = (None, None)
self.chunked = False
sections = None
output = None if __debug__:
self.err_log.debug('Getting sock_file') # Build our file-like object
if PY3K:
sock_file = conn.makefile(mode='rb', buffering=BUF_SIZE)
else:
sock_file = conn.makefile(BUF_SIZE) try:
# Read the headers and build our WSGI environment
self.environ = environ = self.build_environ(sock_file, conn) # Handle 100 Continue
if environ.get('HTTP_EXPECT', '') == '100-continue':
res = environ['SERVER_PROTOCOL'] + ' 100 Continue\r\n\r\n'
conn.sendall(b(res)) # Send it to our WSGI application
output = self.app(environ, self.start_response) if not hasattr(output, '__len__') and not hasattr(output, '__iter__'):
self.error = ('500 Internal Server Error',
'WSGI applications must return a list or '
'generator type.') if hasattr(output, '__len__'):
sections = len(output) for data in output:
# Don't send headers until body appears
if data:
self.write(data, sections) if not self.headers_sent:
# Send headers if the body was empty
self.send_headers('', sections) if self.chunked and self.request_method != 'HEAD':
# If chunked, send our final chunk length
self.conn.sendall(b('0\r\n\r\n')) # Don't capture exceptions here. The Worker class handles
# them appropriately.
finally:
if __debug__:
self.err_log.debug('Finally closing output and sock_file') if hasattr(output, 'close'):
output.close() sock_file.close() # Monolithic build...end of module: rocket/methods/wsgi.py

在runapp中主要执行了下面的代码:

self.environ = environ = self.build_environ(sock_file, conn)
output = self.app(environ, self.start_response)

第一行是读取客户端的请求,并取得所有的请求头。

第二行是调用请求对应的方法,并取得输出。

app是worker初始化的时候传入的。

web.py 学习(二)Worker的更多相关文章

  1. web.py 学习(-)Rocket web框架

    Rocket是一个轻量级,多线程,符合WSGI规范的web框架. Rocket使用一个线程监听连接,接收到连接之后放到Queue中,有worker线程进行处理. Rocket含有以下属性: metho ...

  2. web.py学习心得

    1.注意判断数字时,如果是get传递的参数,一定要用int转换.不然出错. 2.$var 定义时,冒号后的内容不是python内容,需加上$符号.如$var naviId:$naviId. 3.各个模 ...

  3. web.py学习遇到的问题

    刚配置好了web.py运行所需要的环境,试着运行一个入门小实例,结果遇到了异常提示.不知道是什么原因导致的(是环境没配置好?还是……),暂时做个标记,记录一下. 运行的代码 import web ur ...

  4. Web安全学习二

    目录 常见漏洞攻防 SQL注入 注入分类 按技巧分类 按获取数据的方式分类 注入检测 权限提升 数据库检测 绕过技巧 CheatSheet SQL Server Payload MySQL Paylo ...

  5. WEB安全学习二、注入工具 sqlmap的使用

    使用的是Kali Linux 系统,系统中默认的sqlmap 是安装好了的,电脑上没有安装sqlmap,自己百度  ,需要python的环境 使用 命令   sqlmap -h 可以查看   sqlm ...

  6. web前端学习(二) javascript对象和原型继承

    目录 1. JavaScrpt对象 2. 原型对象和继承 3. 对象的克隆 (1)javascript对象 在JS中,对象是属性的容器.对于单个对象来说,都由属性名和属性值构成:其中属性名需要是标识符 ...

  7. JFinal Web开发学习(二)目录、架构、package设计

    package分类 config是JFinal的项目配置 controller是控制器 handler可以设置全局处理器,例如判断用户请求中是否直接请求 FreeMarker的模板文件ftl或者htm ...

  8. web.py simpletodo 例子

    一个很好的例子: 许多新手,特别是从 ASP/PHP/JSP 转过来的同学,经常问下面这几个问题: 所有东西都放在一个 code.py 中呀?我有好多东西该如何部署我的代码? 是不是 /index 对 ...

  9. web.py开发

    web.py需要使用python2.X,所以安装python版本2.7.9 web.py 是一个轻量级Python web框架,它简单而且功能强大 web.py安装 安装python (1)使用pip ...

随机推荐

  1. linux的基本命令

    find: find [路径] [选项] [查找规则] [动作] -print 打印出找到的文件,以\n结束 -print0 打印出找到的文件,无换行 -exec command 对找到的文件执行命令 ...

  2. Front-End(五)——工具使用

    mac端推荐使用sublime+emmet. 环境搭建 sublime 官网下载sublime text 02或者03,03现在(2016.07)还是测试版,我使用的是text02. emmet su ...

  3. SystemInfo获取设备系统参数

    using UnityEngine; using System.Collections; using System.Collections.Generic; publicclassGameContro ...

  4. ueditor的工具按钮配置

    定制工具栏图标 UEditor 工具栏上的按钮列表可以自定义配置,只需要通过修改配置项就可以实现需求 配置项修改说明 修改配置项的方法: 1. 方法一:修改 ueditor.config.js 里面的 ...

  5. linux重要命令

    echo echo命令用于在终端显示字符串或输出变量提取后的值,格式为:"echo [字符串 | $变量]". 将指定字符串输出到终端屏幕: [root@linuxprobe ~] ...

  6. iBeacon在iPhone锁屏下有几率扫描不到蓝牙设备以及折中解决方案

    前言: 这个问题的确困扰了很久,查了国内外各种论坛.社区,都没找到一个有效的解决办法. 所幸后来用一种折中的办法解决了,但也不是从技术根本上解决的,所以有解决的兄弟还望指导下. 正文如下: 一.需求描 ...

  7. asp.net <% = #区别

    <% = %>是将网页中定义的变量的值赋给控件: 例如:<input name="T_ClientAdd" type="text" id=&q ...

  8. ligerUI实现分页

    在公司实习看到公司框架里使用了ligerUI的grid进行分页,个人感觉挺好用的,自己摸索着实现了一遍记录下来 简单来说,liger grid 就是提交准备好的数据到指定的目标请求数据,拿到数据以后, ...

  9. linode开通Paypal付款方式

    vps服务器品牌linode近期新闻不断.今天是linode成立13周年,全部套餐免费升级翻倍内存,所以现在linode最低配置套餐内存是2GB,每月2TB流量,40Gb机房带宽,非常超值. 长期以来 ...

  10. 《HTML5与CSS3权威指南》读书笔记(上册)—HTML5篇

    豆瓣上的评分少且评价不太好,阅读当中发现几处刊物上的小问题,不过线下口碑貌似不错,基本上人手一本 上册五百多页,主讲H5,分为标签,本地存储,离线应用程序,新的API,获取地理位置信息标签包含表单,C ...