[Zope-Checkins] CVS: Zope/lib/python/ZServer/medusa/sendfile - README:1.1.2.1 asynchat_sendfile.py:1.1.2.1 sendfilemodule.c:1.1.2.1 test_sendfile.py:1.1.2.1
Chris McDonough
chrism@zope.com
Tue, 17 Sep 2002 01:16:10 -0400
Update of /cvs-repository/Zope/lib/python/ZServer/medusa/sendfile
In directory cvs.zope.org:/tmp/cvs-serv12650/lib/python/ZServer/medusa/sendfile
Added Files:
Tag: chrism-install-branch
README asynchat_sendfile.py sendfilemodule.c test_sendfile.py
Log Message:
Moved ZServer into lib/python.
=== Added File Zope/lib/python/ZServer/medusa/sendfile/README ===
# -*- Mode: text; tab-width:4 -*-
#
# $Id: README,v 1.1.2.1 2002/09/17 05:16:09 chrism Exp $
#
Preliminary support for asynchronous sendfile() on linux and freebsd.
Not heavily tested yet.
-Sam
=== Added File Zope/lib/python/ZServer/medusa/sendfile/asynchat_sendfile.py ===
# -*- Mode: Python; tab-width: 4 -*-
RCS_ID = '$Id: asynchat_sendfile.py,v 1.1.2.1 2002/09/17 05:16:09 chrism Exp $'
import sendfile
import asynchat
async_chat = asynchat.async_chat
# we can't call sendfile() until ac_out_buffer is empty.
class async_chat_with_sendfile (async_chat):
# if we are in the middle of sending a file, this will be overriden
_sendfile = None
def push_sendfile (self, fd, offset, bytes, callback=None):
# we set out_buffer_size to zero in order to keep async_chat
# from calling refill_buffer until the whole file has been sent.
self._saved_obs = self.ac_out_buffer_size
self.ac_out_buffer_size = 0
self._sendfile = (fd, offset, bytes, callback)
def initiate_send (self):
if self._sendfile is None:
async_chat.initiate_send (self)
else:
if len(self.ac_out_buffer):
async_chat.initiate_send (self)
else:
fd, offset, bytes, callback = self._sendfile
me = self.socket.fileno()
try:
sent = sendfile.sendfile (fd, me, offset, bytes)
offset = offset + sent
bytes = bytes - sent
if bytes:
self._sendfile = (fd, offset, bytes, callback)
else:
self._sendfile = None
self.ac_out_buffer_size = self._saved_obs
if callback is not None:
# success
callback (1, fd)
except:
self._sendfile = None
self.ac_out_buffer_size = self._saved_obs
# failure
if callback is not None:
callback (0, fd)
# here's how you might use this:
# fd = os.open (filename, os.O_RDONLY, 0644)
# size = os.lseek (fd, 0, 2)
# os.lseek (fd, 0, 0)
# self.push ('%08x' % size)
# self.push_sendfile (fd, 0, size)
=== Added File Zope/lib/python/ZServer/medusa/sendfile/sendfilemodule.c ===
// -*- Mode: C; tab-width: 8 -*-
#include "Python.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
//
// sendfile(), meant for use with a non-blocking socket
//
#if defined (__linux__)
// these are taken from linux/socket.h, for some reason including that file doesn't work here.
#define SOL_TCP 6
#define TCP_CORK 3
// non-blocking TCP_CORK sucks mugwump jism.
static PyObject *
py_sendfile (PyObject * self, PyObject * args)
{
int fd;
int sock;
long o,n;
char * head = NULL;
size_t head_len = 0;
char * tail = NULL;
size_t tail_len = 0;
if (!PyArg_ParseTuple (
args, "iill|s#s#",
&fd, &sock, &o, &n,
&head, &head_len,
&tail, &tail_len
)) {
return NULL;
} else {
off_t offset = o;
size_t nbytes = n;
int orig_cork = 1;
int orig_cork_len = sizeof(int);
PyObject * py_result = NULL;
ssize_t sent_h = 0;
ssize_t sent_f = 0;
ssize_t sent_t = 0;
if (head || tail) {
int cork = 1;
// first, fetch the original setting
getsockopt (sock, SOL_TCP, TCP_CORK, (void*)&orig_cork, &orig_cork_len);
setsockopt (sock, SOL_TCP, TCP_CORK, (void*)&cork, sizeof(cork));
}
// send header
if (head) {
sent_h = send (sock, head, head_len, 0);
if (sent_h < 0) {
PyErr_SetFromErrno (PyExc_OSError);
py_result = NULL;
goto done;
} else if (sent_h < head_len) {
py_result = PyInt_FromLong (sent_h);
goto done;
}
}
// send file
sent_f = sendfile (sock, fd, &offset, nbytes);
if (sent_f < 0) {
PyErr_SetFromErrno (PyExc_OSError);
py_result = NULL;
goto done;
} else if (sent_f < nbytes) {
py_result = PyInt_FromLong (sent_h + sent_f);
goto done;
}
// send trailer
if (tail) {
sent_t = send (sock, tail, tail_len, 0);
if (sent_t < 0) {
PyErr_SetFromErrno (PyExc_OSError);
py_result = NULL;
goto done;
}
}
py_result = PyInt_FromLong (sent_h + sent_f + sent_t);
done:
if (head || tail) {
setsockopt (sock, SOL_TCP, TCP_CORK, (void*)&orig_cork, sizeof(orig_cork));
}
return py_result;
}
}
#elif defined (__FreeBSD__)
// XXX support the header/trailer stuff.
static PyObject *
py_sendfile (PyObject * self, PyObject * args)
{
int fd;
int sock;
long o,n;
char * head = NULL;
size_t head_len = 0;
char * tail = NULL;
size_t tail_len = 0;
if (!PyArg_ParseTuple (
args, "iill|s#s#",
&fd, &sock, &o, &n,
&head, &head_len,
&tail, &tail_len
)
) {
return NULL;
} else {
off_t offset = o;
size_t nbytes = n;
off_t sbytes;
int result;
if (head || tail) {
struct iovec ivh = {head, head_len};
struct iovec ivt = {tail, tail_len};
struct sf_hdtr hdtr = {&ivh, 1, &ivt, 1};
result = sendfile (fd, sock, offset, nbytes, &hdtr, &sbytes, 0);
} else {
result = sendfile (fd, sock, offset, nbytes, NULL, &sbytes, 0);
}
if (result == -1) {
if (errno == EAGAIN) {
return PyInt_FromLong (sbytes);
} else {
PyErr_SetFromErrno (PyExc_OSError);
}
} else {
return PyInt_FromLong (sbytes);
}
}
}
#else
#error ("unknown platform")
#endif
// <off_t> and <size_t> are 64 bits on FreeBSD.
// If you need this feature, then look at PyLong_FromUnsignedLongLong.
// You'll have to specifically check for the PyLong type, etc...
// Also, LONG_LONG is a configure variable (i.e., the conversion function
// might not be available).
static PyMethodDef sendfile_methods[] = {
{"sendfile", py_sendfile, 1},
{NULL, NULL} /* sentinel */
};
DL_EXPORT(void)
initsendfile()
{
PyObject *m, *d;
m = Py_InitModule("sendfile", sendfile_methods);
}
=== Added File Zope/lib/python/ZServer/medusa/sendfile/test_sendfile.py ===
# -*- Mode: Python; tab-width: 4 -*-
RCS_ID = '$Id: test_sendfile.py,v 1.1.2.1 2002/09/17 05:16:09 chrism Exp $'
import asyncore
import os
import socket
import string
# server: just run the script with no args
# client: python test_sendfile.py -c <remote-host> <remote-filename>
if __name__ == '__main__':
import sys
if '-c' in sys.argv:
import operator
import socket
s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
host = sys.argv[2]
s.connect ((host, 9009))
fname = sys.argv[3]
s.send (fname + '\r\n')
size = string.atoi (s.recv(8), 16)
total = 0
blocks = []
while (total < size):
block = s.recv (8192)
if not block:
break
else:
total = total + len(block)
blocks.append (block)
import sys
for b in blocks:
sys.stdout.write (b)
else:
import asynchat_sendfile
class test_channel (asynchat_sendfile.async_chat_with_sendfile):
def __init__ (self, conn, addr):
asynchat_sendfile.async_chat_with_sendfile.__init__ (self, conn)
self.set_terminator ('\r\n')
self.buffer = ''
def collect_incoming_data (self, data):
self.buffer = self.buffer + data
def found_terminator (self):
filename, self.buffer = self.buffer, ''
if filename:
fd = os.open (filename, os.O_RDONLY, 0644)
size = os.lseek (fd, 0, 2)
os.lseek (fd, 0, 0)
self.push ('%08x' % size)
self.push_sendfile (fd, 0, size, self.sendfile_callback)
self.close_when_done()
else:
self.push ('ok, bye\r\n')
self.close_when_done()
def sendfile_callback (self, success, fd):
os.close (fd)
class test_server (asyncore.dispatcher):
def __init__ (self, addr=('', 9009)):
self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind (addr)
self.listen (2048)
print 'server started on', addr
def handle_accept (self):
conn, addr = self.accept()
test_channel (conn, addr)
test_server()
asyncore.loop()