Removed Green_fileobject and moved its contents to GreenPipe. Removed higher_order_send and higher_order_recv, integrating their contents with the appropriate methods on GreenSocket and GreenPipe. Fixed up callsites that were depending on various aspects of Green_fileobject's behavior. Added a backdoor unit test.

This commit is contained in:
Ryan Williams
2010-01-02 22:51:34 -08:00
parent 9791fefac6
commit a19a7f3858
5 changed files with 263 additions and 262 deletions

View File

@@ -3,7 +3,7 @@ import sys
import errno
from code import InteractiveConsole
from eventlet import api
from eventlet import api, hubs
from eventlet.support import greenlets
try:
@@ -16,25 +16,34 @@ except AttributeError:
sys.ps2 = '... '
class FileProxy(object):
def __init__(self, f):
self.f = f
def writeflush(*a, **kw):
f.write(*a, **kw)
f.flush()
self.fixups = {
'softspace': 0,
'isatty': lambda: True,
'flush': lambda: None,
'write': writeflush,
'readline': lambda *a: f.readline(*a).replace('\r\n', '\n'),
}
def __getattr__(self, attr):
fixups = object.__getattribute__(self, 'fixups')
if attr in fixups:
return fixups[attr]
f = object.__getattribute__(self, 'f')
return getattr(f, attr)
class SocketConsole(greenlets.greenlet):
def __init__(self, desc, hostport, locals):
self.hostport = hostport
self.locals = locals
# mangle the socket
self.desc = desc
readline = desc.readline
self.old = {}
self.fixups = {
'softspace': 0,
'isatty': lambda: True,
'flush': lambda: None,
'readline': lambda *a: readline(*a).replace('\r\n', '\n'),
}
for key, value in self.fixups.iteritems():
if hasattr(desc, key):
self.old[key] = getattr(desc, key)
setattr(desc, key, value)
self.desc = FileProxy(desc)
greenlets.greenlet.__init__(self)
def run(self):
@@ -55,15 +64,6 @@ class SocketConsole(greenlets.greenlet):
def finalize(self):
# restore the state of the socket
for key in self.fixups:
try:
value = self.old[key]
except KeyError:
delattr(self.desc, key)
else:
setattr(self.desc, key, value)
self.fixups.clear()
self.old.clear()
self.desc = None
print "backdoor closed to %s:%s" % self.hostport
@@ -73,18 +73,18 @@ def backdoor_server(sock, locals=None):
accepting connections and running backdoor consoles for each client that
connects.
"""
print "backdoor server listening on %s:%s" % server.getsockname()
print "backdoor server listening on %s:%s" % sock.getsockname()
try:
try:
while True:
socketpair = server.accept()
socketpair = sock.accept()
backdoor(socketpair, locals)
except socket.error, e:
# Broken pipe means it was shutdown
if e[0] != errno.EPIPE:
raise
finally:
server.close()
sock.close()
def backdoor((conn, addr), locals=None):
@@ -95,8 +95,7 @@ def backdoor((conn, addr), locals=None):
"""
host, port = addr
print "backdoor to %s:%s" % (host, port)
fl = conn.makeGreenFile("rw")
fl.newlines = '\n'
fl = conn.makefile("rw")
console = SocketConsole(fl, (host, port), locals)
hub = hubs.get_hub()
hub.schedule_call_global(0, console.switch)

View File

@@ -15,56 +15,11 @@ import warnings
from errno import EWOULDBLOCK, EAGAIN
__all__ = ['GreenSocket', 'GreenPipe']
__all__ = ['GreenSocket', 'GreenPipe', 'shutdown_safe']
def higher_order_recv(recv_func):
def recv(self, buflen, flags=0):
if self.act_non_blocking:
return self.fd.recv(buflen, flags)
buf = self.recvbuffer
if buf:
chunk, self.recvbuffer = buf[:buflen], buf[buflen:]
return chunk
fd = self.fd
bytes = recv_func(fd, buflen, flags)
if self.gettimeout():
end = time.time()+self.gettimeout()
else:
end = None
timeout = None
while bytes is None:
try:
if end:
timeout = end - time.time()
trampoline(fd, read=True, timeout=timeout, timeout_exc=socket.timeout)
except socket.timeout:
raise
except socket.error, e:
if e[0] == errno.EPIPE:
bytes = ''
else:
raise
else:
bytes = recv_func(fd, buflen, flags)
self.recvcount += len(bytes)
return bytes
return recv
CONNECT_ERR = set((errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK))
CONNECT_SUCCESS = set((0, errno.EISCONN))
def higher_order_send(send_func):
def send(self, data, flags=0):
if self.act_non_blocking:
return self.fd.send(data, flags)
count = send_func(self.fd, data, flags)
if not count:
return 0
self.sendcount += count
return count
return send
CONNECT_ERR = (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK)
CONNECT_SUCCESS = (0, errno.EISCONN)
def socket_connect(descriptor, address):
err = descriptor.connect_ex(address)
if err in CONNECT_ERR:
@@ -83,60 +38,15 @@ def socket_accept(descriptor):
raise
def socket_send(descriptor, data, flags=0):
try:
return descriptor.send(data, flags)
except socket.error, e:
if e[0] == errno.EWOULDBLOCK or e[0] == errno.ENOTCONN:
return 0
raise
if sys.platform[:3]=="win":
# winsock sometimes throws ENOTCONN
SOCKET_BLOCKING = (errno.EWOULDBLOCK,)
SOCKET_CLOSED = (errno.ECONNRESET, errno.ENOTCONN, errno.ESHUTDOWN)
SOCKET_BLOCKING = set((errno.EWOULDBLOCK,))
SOCKET_CLOSED = set((errno.ECONNRESET, errno.ENOTCONN, errno.ESHUTDOWN))
else:
# oddly, on linux/darwin, an unconnected socket is expected to block,
# so we treat ENOTCONN the same as EWOULDBLOCK
SOCKET_BLOCKING = (errno.EWOULDBLOCK, errno.ENOTCONN)
SOCKET_CLOSED = (errno.ECONNRESET, errno.ESHUTDOWN)
def socket_recv(descriptor, buflen, flags=0):
try:
return descriptor.recv(buflen, flags)
except socket.error, e:
if e[0] in SOCKET_BLOCKING:
return None
if e[0] in SOCKET_CLOSED:
return ''
raise
def file_recv(fd, buflen, flags=0):
try:
return fd.read(buflen)
except IOError, e:
if e[0] == EAGAIN:
return None
return ''
except socket.error, e:
if e[0] == errno.EPIPE:
return ''
raise
def file_send(fd, data, flags=0):
try:
fd.write(data)
fd.flush()
return len(data)
except IOError, e:
if e[0] == EAGAIN:
return 0
except ValueError, e:
written = 0
except socket.error, e:
if e[0] == errno.EPIPE:
written = 0
SOCKET_BLOCKING = set((errno.EWOULDBLOCK, errno.ENOTCONN))
SOCKET_CLOSED = set((errno.ECONNRESET, errno.ESHUTDOWN, errno.EPIPE))
def set_nonblocking(fd):
@@ -176,9 +86,6 @@ class GreenSocket(object):
set_nonblocking(fd)
self.fd = fd
self._fileno = fd.fileno()
self.sendcount = 0
self.recvcount = 0
self.recvbuffer = ''
self.closed = False
self.timeout = socket.getdefaulttimeout()
@@ -303,9 +210,28 @@ class GreenSocket(object):
return socket._fileobject(self.dup(), mode, bufsize)
def makeGreenFile(self, mode='r', bufsize=-1):
return Green_fileobject(self.dup())
warnings.warn("makeGreenFile has been deprecated, please use "
"makefile instead", DeprecationWarning, stacklevel=2)
return self.makefile(mode, bufsize)
recv = higher_order_recv(socket_recv)
def recv(self, buflen, flags=0):
fd = self.fd
if self.act_non_blocking:
return fd.recv(buflen, flags)
while True:
try:
return fd.recv(buflen, flags)
except socket.error, e:
if e[0] in SOCKET_BLOCKING:
pass
elif e[0] in SOCKET_CLOSED:
return ''
else:
raise
trampoline(fd,
read=True,
timeout=self.gettimeout(),
timeout_exc=socket.timeout)
def recvfrom(self, *args):
if not self.act_non_blocking:
@@ -322,13 +248,25 @@ class GreenSocket(object):
trampoline(self.fd, read=True, timeout=self.gettimeout(), timeout_exc=socket.timeout)
return self.fd.recv_into(*args)
send = higher_order_send(socket_send)
def send(self, data, flags=0):
fd = self.fd
if self.act_non_blocking:
return fd.send(data, flags)
try:
return fd.send(data, flags)
except socket.error, e:
if e[0] in SOCKET_BLOCKING:
return 0
raise
def sendall(self, data, flags=0):
fd = self.fd
tail = self.send(data, flags)
while tail < len(data):
trampoline(self.fd, write=True, timeout_exc=socket.timeout)
trampoline(fd,
write=True,
timeout=self.gettimeout(),
timeout_exc=socket.timeout)
tail += self.send(data[tail:], flags)
def sendto(self, *args):
@@ -371,47 +309,78 @@ class GreenSocket(object):
return self.timeout
class Green_fileobject(object):
"""Green version of socket._fileobject, for use only with regular
sockets."""
class GreenPipe(object):
newlines = '\r\n'
mode = 'wb+'
def __init__(self, fd):
if isinstance(fd, GreenSocket):
set_nonblocking(fd.fd)
else:
set_nonblocking(fd)
self.sock = fd
set_nonblocking(fd)
self.fd = fd
self.closed = False
self.recvbuffer = ''
def close(self):
self.sock.close()
self.fd.close()
self.closed = True
def fileno(self):
return self.sock.fileno()
return self.fd.fileno()
# TODO next
def read(self, buflen, flags=0):
fd = self.fd
if buflen is None:
buflen = BUFFER_SIZE
buf = self.recvbuffer
if buf:
chunk, self.recvbuffer = buf[:buflen], buf[buflen:]
return chunk
while True:
try:
return fd.read(buflen)
except IOError, e:
if e[0] != EAGAIN:
return ''
except socket.error, e:
if e[0] == errno.EPIPE:
return ''
raise
trampoline(fd, read=True)
def write(self, data, flags=0):
fd = self.fd
tail = 0
len_data = len(data)
while tail < len_data:
tosend = data[tail:]
try:
fd.write(tosend)
fd.flush()
tail += len(tosend)
if tail == len_data:
return len_data
except IOError, e:
if e[0] != EAGAIN:
raise
except ValueError, e:
pass
except socket.error, e:
if e[0] != errno.EPIPE:
raise
trampoline(fd, write=True)
def flush(self):
pass
def write(self, data):
return self.sock.sendall(data)
def readuntil(self, terminator, size=None):
buf, self.sock.recvbuffer = self.sock.recvbuffer, ''
buf, self.recvbuffer = self.recvbuffer, ''
checked = 0
if size is None:
while True:
found = buf.find(terminator, checked)
if found != -1:
found += len(terminator)
chunk, self.sock.recvbuffer = buf[:found], buf[found:]
chunk, self.recvbuffer = buf[:found], buf[found:]
return chunk
checked = max(0, len(buf) - (len(terminator) - 1))
d = self.sock.recv(BUFFER_SIZE)
d = self.fd.read(BUFFER_SIZE)
if not d:
break
buf += d
@@ -420,25 +389,22 @@ class Green_fileobject(object):
found = buf.find(terminator, checked)
if found != -1:
found += len(terminator)
chunk, self.sock.recvbuffer = buf[:found], buf[found:]
chunk, self.recvbuffer = buf[:found], buf[found:]
return chunk
checked = len(buf)
d = self.sock.recv(BUFFER_SIZE)
d = self.fd.read(BUFFER_SIZE)
if not d:
break
buf += d
chunk, self.sock.recvbuffer = buf[:size], buf[size:]
chunk, self.recvbuffer = buf[:size], buf[size:]
return chunk
def readline(self, size=None):
return self.readuntil(self.newlines, size=size)
def __iter__(self):
return self.xreadlines()
def readlines(self, size=None):
return list(self.xreadlines(size=size))
def xreadlines(self, size=None):
if size is None:
while True:
@@ -458,61 +424,6 @@ class Green_fileobject(object):
for line in lines:
self.write(line)
def read(self, size=None):
if size is not None and not isinstance(size, (int, long)):
raise TypeError('Expecting an int or long for size, got %s: %s' % (type(size), repr(size)))
buf, self.sock.recvbuffer = self.sock.recvbuffer, ''
lst = [buf]
if size is None:
while True:
d = self.sock.recv(BUFFER_SIZE)
if not d:
break
lst.append(d)
else:
buflen = len(buf)
while buflen < size:
d = self.sock.recv(BUFFER_SIZE)
if not d:
break
buflen += len(d)
lst.append(d)
else:
d = lst[-1]
overbite = buflen - size
if overbite:
lst[-1], self.sock.recvbuffer = d[:-overbite], d[-overbite:]
else:
lst[-1], self.sock.recvbuffer = d, ''
return ''.join(lst)
class GreenPipeSocket(GreenSocket):
""" This is a weird class that looks like a socket but expects a file descriptor as an argument instead of a socket.
"""
recv = higher_order_recv(file_recv)
send = higher_order_send(file_send)
class GreenPipe(Green_fileobject):
def __init__(self, fd):
set_nonblocking(fd)
self.fd = GreenPipeSocket(fd)
super(GreenPipe, self).__init__(self.fd)
def recv(self, *args, **kw):
fn = self.recv = self.fd.recv
return fn(*args, **kw)
def send(self, *args, **kw):
fn = self.send = self.fd.send
return fn(*args, **kw)
def flush(self):
self.fd.fd.flush()
# import SSL module here so we can refer to greenio.SSL.exceptionclass
try:

29
tests/backdoor_test.py Normal file
View File

@@ -0,0 +1,29 @@
import eventlet
from eventlet import backdoor
from eventlet.green import socket
from tests import LimitedTestCase, main
class BackdoorTest(LimitedTestCase):
def test_server(self):
listener = socket.socket()
listener.bind(('localhost', 0))
listener.listen(50)
serv = eventlet.spawn(backdoor.backdoor_server, listener)
client = socket.socket()
client.connect(('localhost', listener.getsockname()[1]))
f = client.makefile()
self.assert_('Python' in f.readline())
f.readline() # build info
f.readline() # help info
self.assert_('InteractiveConsole' in f.readline())
self.assertEquals('>>> ', f.read(4))
f.write('print "hi"\n')
f.flush()
self.assertEquals('hi\n', f.readline())
self.assertEquals('>>> ', f.read(4))
if __name__ == '__main__':
main()

View File

@@ -47,11 +47,15 @@ class TestGreenIo(LimitedTestCase):
# by closing the socket prior to using the made file
try:
conn, addr = listener.accept()
fd = conn.makeGreenFile()
fd = conn.makefile()
conn.close()
fd.write('hello\n')
fd.close()
self.assertRaises(socket.error, fd.write, 'a')
# socket._fileobjects are odd: writes don't check
# whether the socket is closed or not, and you get an
# AttributeError during flush if it is closed
fd.write('a')
self.assertRaises(Exception, fd.flush)
self.assertRaises(socket.error, conn.send, 'b')
finally:
listener.close()
@@ -61,19 +65,20 @@ class TestGreenIo(LimitedTestCase):
# by closing the made file and then sending a character
try:
conn, addr = listener.accept()
fd = conn.makeGreenFile()
fd = conn.makefile()
fd.write('hello')
fd.close()
conn.send('\n')
conn.close()
self.assertRaises(socket.error, fd.write, 'a')
fd.write('a')
self.assertRaises(Exception, fd.flush)
self.assertRaises(socket.error, conn.send, 'b')
finally:
listener.close()
def did_it_work(server):
client = api.connect_tcp(('127.0.0.1', server.getsockname()[1]))
fd = client.makeGreenFile()
fd = client.makefile()
client.close()
assert fd.readline() == 'hello\n'
assert fd.read() == ''
@@ -96,16 +101,17 @@ class TestGreenIo(LimitedTestCase):
# closing the file object should close everything
try:
conn, addr = listener.accept()
conn = conn.makeGreenFile()
conn = conn.makefile()
conn.write('hello\n')
conn.close()
self.assertRaises(socket.error, conn.write, 'a')
conn.write('a')
self.assertRaises(Exception, conn.flush)
finally:
listener.close()
server = api.tcp_listener(('0.0.0.0', 0))
killer = coros.execute(accept_once, server)
client = api.connect_tcp(('127.0.0.1', server.getsockname()[1]))
fd = client.makeGreenFile()
fd = client.makefile()
client.close()
assert fd.read() == 'hello\n'
assert fd.read() == ''

View File

@@ -83,7 +83,7 @@ class ConnectionClosed(Exception):
def read_http(sock):
fd = sock.makeGreenFile()
fd = sock.makefile()
try:
response_line = fd.readline()
except socket.error, exc:
@@ -92,11 +92,19 @@ def read_http(sock):
raise
if not response_line:
raise ConnectionClosed
raw_headers = fd.readuntil('\r\n\r\n').strip()
#print "R", response_line, raw_headers
header_lines = []
while True:
line = fd.readline()
if line == '\r\n':
break
else:
header_lines.append(line)
headers = dict()
for x in raw_headers.split('\r\n'):
#print "X", x
for x in header_lines:
x = x.strip()
if not x:
continue
key, value = x.split(': ', 1)
headers[key.lower()] = value
@@ -134,8 +142,9 @@ class TestHttpd(LimitedTestCase):
sock = api.connect_tcp(
('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET / HTTP/1.0\r\nHost: localhost\r\n\r\n')
fd.flush()
result = fd.read()
fd.close()
## The server responds with the maximum version it supports
@@ -146,10 +155,12 @@ class TestHttpd(LimitedTestCase):
sock = api.connect_tcp(
('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
fd.flush()
read_http(sock)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
fd.flush()
read_http(sock)
fd.close()
@@ -158,8 +169,9 @@ class TestHttpd(LimitedTestCase):
sock = api.connect_tcp(
('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
fd.flush()
cancel = api.exc_after(1, RuntimeError)
self.assertRaises(TypeError, fd.read, "This shouldn't work")
cancel.cancel()
@@ -169,12 +181,15 @@ class TestHttpd(LimitedTestCase):
sock = api.connect_tcp(
('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
fd.flush()
read_http(sock)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
fd.flush()
read_http(sock)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
fd.flush()
self.assertRaises(ConnectionClosed, read_http, sock)
fd.close()
@@ -194,8 +209,9 @@ class TestHttpd(LimitedTestCase):
path_parts.append('path')
path = '/'.join(path_parts)
request = 'GET /%s HTTP/1.0\r\nHost: localhost\r\n\r\n' % path
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write(request)
fd.flush()
result = fd.readline()
if result:
# windows closes the socket before the data is flushed,
@@ -220,8 +236,9 @@ class TestHttpd(LimitedTestCase):
'Content-Length: 3',
'',
'a=a'))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write(request)
fd.flush()
# send some junk after the actual request
fd.write('01234567890123456789')
@@ -233,12 +250,15 @@ class TestHttpd(LimitedTestCase):
sock = api.connect_tcp(
('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
fd.flush()
response_line_200,_,_ = read_http(sock)
fd.write('GET /notexist HTTP/1.1\r\nHost: localhost\r\n\r\n')
fd.flush()
response_line_404,_,_ = read_http(sock)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
fd.flush()
response_line_test,_,_ = read_http(sock)
self.assertEqual(response_line_200,response_line_test)
fd.close()
@@ -248,8 +268,9 @@ class TestHttpd(LimitedTestCase):
sock = api.connect_tcp(
('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
fd.flush()
self.assert_('Transfer-Encoding: chunked' in fd.read())
def test_010_no_chunked_http_1_0(self):
@@ -257,8 +278,9 @@ class TestHttpd(LimitedTestCase):
sock = api.connect_tcp(
('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET / HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n')
fd.flush()
self.assert_('Transfer-Encoding: chunked' not in fd.read())
def test_011_multiple_chunks(self):
@@ -266,9 +288,16 @@ class TestHttpd(LimitedTestCase):
sock = api.connect_tcp(
('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
headers = fd.readuntil('\r\n\r\n')
fd.flush()
headers = ''
while True:
line = fd.readline()
if line == '\r\n':
break
else:
headers += line
self.assert_('Transfer-Encoding: chunked' in headers)
chunks = 0
chunklen = int(fd.readline(), 16)
@@ -316,43 +345,54 @@ class TestHttpd(LimitedTestCase):
def test_014_chunked_post(self):
self.site.application = chunked_post
sock = api.connect_tcp(('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('PUT /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
'Transfer-Encoding: chunked\r\n\r\n'
'2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n')
fd.readuntil('\r\n\r\n')
fd.flush()
while True:
if fd.readline() == '\r\n':
break
response = fd.read()
self.assert_(response == 'oh hai', 'invalid response %s' % response)
sock = api.connect_tcp(('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('PUT /b HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
'Transfer-Encoding: chunked\r\n\r\n'
'2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n')
fd.readuntil('\r\n\r\n')
fd.flush()
while True:
if fd.readline() == '\r\n':
break
response = fd.read()
self.assert_(response == 'oh hai', 'invalid response %s' % response)
sock = api.connect_tcp(('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('PUT /c HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
'Transfer-Encoding: chunked\r\n\r\n'
'2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n')
fd.readuntil('\r\n\r\n')
fd.flush()
while True:
if fd.readline() == '\r\n':
break
response = fd.read(8192)
self.assert_(response == 'oh hai', 'invalid response %s' % response)
def test_015_write(self):
self.site.application = use_write
sock = api.connect_tcp(('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
fd.flush()
response_line, headers, body = read_http(sock)
self.assert_('content-length' in headers)
sock = api.connect_tcp(('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET /b HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
fd.flush()
response_line, headers, body = read_http(sock)
self.assert_('transfer-encoding' in headers)
self.assert_(headers['transfer-encoding'] == 'chunked')
@@ -367,10 +407,17 @@ class TestHttpd(LimitedTestCase):
return ['testing']
self.site.application = wsgi_app
sock = api.connect_tcp(('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
headerlines = fd.readuntil('\r\n\r\n').splitlines()
self.assertEquals(1, len([l for l in headerlines
fd.flush()
header_lines = []
while True:
line = fd.readline()
if line == '\r\n':
break
else:
header_lines.append(line)
self.assertEquals(1, len([l for l in header_lines
if l.lower().startswith('content-length')]))
def test_017_ssl_zeroreturnerror(self):
@@ -413,14 +460,19 @@ class TestHttpd(LimitedTestCase):
sock = api.connect_tcp(
('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET / HTTP/1.0\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n')
self.assert_('connection: keep-alive' in
fd.readuntil('\r\n\r\n').lower())
fd.flush()
response_line, headers, body = read_http(sock)
self.assert_('connection' in headers)
self.assertEqual('keep-alive', headers['connection'])
# repeat request to verify connection is actually still open
fd.write('GET / HTTP/1.0\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n')
self.assert_('connection: keep-alive' in
fd.readuntil('\r\n\r\n').lower())
fd.write('GET / HTTP/1.0\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n')
fd.flush()
response_line, headers, body = read_http(sock)
self.assert_('connection' in headers)
self.assertEqual('keep-alive', headers['connection'])
def test_019_fieldstorage_compat(self):
def use_fieldstorage(environ, start_response):
@@ -434,13 +486,14 @@ class TestHttpd(LimitedTestCase):
sock = api.connect_tcp(
('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('POST / HTTP/1.1\r\n'
'Host: localhost\r\n'
'Connection: close\r\n'
'Transfer-Encoding: chunked\r\n\r\n'
'2\r\noh\r\n'
'4\r\n hai\r\n0\r\n\r\n')
fd.flush()
self.assert_('hello!' in fd.read())
def test_020_x_forwarded_for(self):
@@ -484,11 +537,12 @@ class TestHttpd(LimitedTestCase):
return []
self.site.application = clobberin_time
sock = api.connect_tcp(('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET / HTTP/1.1\r\n'
'Host: localhost\r\n'
'Connection: close\r\n'
'\r\n\r\n')
fd.flush()
self.assert_('200 OK' in fd.read())
def test_022_custom_pool(self):
@@ -511,8 +565,9 @@ class TestHttpd(LimitedTestCase):
# this stuff is copied from test_001_server, could be better factored
sock = api.connect_tcp(
('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET / HTTP/1.0\r\nHost: localhost\r\n\r\n')
fd.flush()
result = fd.read()
fd.close()
self.assert_(result.startswith('HTTP'), result)
@@ -521,8 +576,9 @@ class TestHttpd(LimitedTestCase):
def test_023_bad_content_length(self):
sock = api.connect_tcp(
('localhost', self.port))
fd = sock.makeGreenFile()
fd = sock.makefile()
fd.write('GET / HTTP/1.0\r\nHost: localhost\r\nContent-length: argh\r\n\r\n')
fd.flush()
result = fd.read()
fd.close()
self.assert_(result.startswith('HTTP'), result)