summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Bicking <ian@ianbicking.org>2005-08-15 16:49:21 +0000
committerIan Bicking <ian@ianbicking.org>2005-08-15 16:49:21 +0000
commite41dd70c39b4dfa6b0c69e0f1e6bbc34489f465d (patch)
tree46ce6255d1be8678e8aa99c0b187b2a55e62027a
parent23ba269d2c0959358f0753221b0b6ae9709a2c46 (diff)
downloadpaste-git-e41dd70c39b4dfa6b0c69e0f1e6bbc34489f465d.tar.gz
Added CGI proxy application
-rw-r--r--paste/cgiapp.py88
-rwxr-xr-xpaste/tests/cgiapp_data/error.cgi3
-rwxr-xr-xpaste/tests/cgiapp_data/form.cgi12
-rwxr-xr-xpaste/tests/cgiapp_data/ok.cgi6
-rwxr-xr-xpaste/tests/cgiapp_data/stderr.cgi8
-rw-r--r--paste/tests/test_cgiapp.py35
6 files changed, 152 insertions, 0 deletions
diff --git a/paste/cgiapp.py b/paste/cgiapp.py
new file mode 100644
index 0000000..edc156a
--- /dev/null
+++ b/paste/cgiapp.py
@@ -0,0 +1,88 @@
+"""
+Application that runs a CGI script.
+"""
+import os
+import subprocess
+
+__all__ = ['CGIError', 'CGIApplication']
+
+class CGIError(Exception):
+ pass
+
+class CGIApplication(object):
+
+ """
+ This object acts as a proxy to a CGI application. You pass in the
+ script path (``script``), an optional path to search for the
+ script (if the name isn't absolute) (``path``). If you don't give
+ a path, then ``$PATH`` will be used.
+ """
+
+ def __init__(self, script, path=None,
+ include_os_environ=True):
+ self.script_filename = script
+ if isinstance(path, (str, unicode)):
+ path = [path]
+ if path is None:
+ path = os.environ.get('PATH', '').split(':')
+ self.path = path
+ if os.path.abspath(script) != script:
+ # relative path
+ for path_dir in self.path:
+ if os.path.exists(os.path.join(path_dir, script)):
+ self.script = os.path.join(path_dir, script)
+ break
+ else:
+ raise CGIError(
+ "Script %r not found in path %r"
+ % (script, self.path))
+ else:
+ self.script = script
+ self.include_os_environ = include_os_environ
+
+ def __call__(self, environ, start_response):
+ if self.include_os_environ:
+ cgi_environ = os.environ.copy()
+ else:
+ cgi_environ = {}
+ for name in environ:
+ # Should unicode values be encoded?
+ if (name.upper() == name
+ and isinstance(environ[name], str)):
+ cgi_environ[name] = environ[name]
+ # Default status in CGI:
+ status = '200 OK'
+ headers = []
+ proc = subprocess.Popen(
+ [self.script],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ env=cgi_environ,
+ cwd=os.path.dirname(self.script),
+ )
+ proc.stdin.write(environ['wsgi.input'].read())
+ stdout = proc.stdout
+ while 1:
+ line = stdout.readline()
+ line = line.rstrip('\n').rstrip('\r')
+ if not line:
+ break
+ if ':' not in line:
+ raise CGIError(
+ "Bad header line: %r" % line)
+ name, value = line.split(':', 1)
+ value = value.lstrip()
+ name = name.strip()
+ if name.lower() == 'status':
+ status = value
+ else:
+ headers.append((name, value))
+ writer = start_response(status, headers)
+ while 1:
+ data = stdout.read(4096)
+ if not data:
+ break
+ writer(data)
+ environ['wsgi.errors'].write(proc.stderr.read())
+ return []
diff --git a/paste/tests/cgiapp_data/error.cgi b/paste/tests/cgiapp_data/error.cgi
new file mode 100755
index 0000000..5afc9c9
--- /dev/null
+++ b/paste/tests/cgiapp_data/error.cgi
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+
+print 'hey you!'
diff --git a/paste/tests/cgiapp_data/form.cgi b/paste/tests/cgiapp_data/form.cgi
new file mode 100755
index 0000000..6d2e038
--- /dev/null
+++ b/paste/tests/cgiapp_data/form.cgi
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+
+import cgi
+
+print 'Content-type: text/plain'
+print
+
+form = cgi.FieldStorage()
+
+print 'Filename:', form['up'].filename
+print 'Name:', form['name'].value
+print 'Content:', form['up'].file.read()
diff --git a/paste/tests/cgiapp_data/ok.cgi b/paste/tests/cgiapp_data/ok.cgi
new file mode 100755
index 0000000..8b8eb29
--- /dev/null
+++ b/paste/tests/cgiapp_data/ok.cgi
@@ -0,0 +1,6 @@
+#!/usr/bin/env python
+
+print 'Content-type: text/html; charset=UTF-8'
+print 'Status: 200 Okay'
+print
+print 'This is the body'
diff --git a/paste/tests/cgiapp_data/stderr.cgi b/paste/tests/cgiapp_data/stderr.cgi
new file mode 100755
index 0000000..89dae0a
--- /dev/null
+++ b/paste/tests/cgiapp_data/stderr.cgi
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+
+import sys
+print 'Status: 500 Server Error'
+print 'Content-type: text/html'
+print
+print 'There was an error'
+print >> sys.stderr, 'some data on the error'
diff --git a/paste/tests/test_cgiapp.py b/paste/tests/test_cgiapp.py
new file mode 100644
index 0000000..133cd87
--- /dev/null
+++ b/paste/tests/test_cgiapp.py
@@ -0,0 +1,35 @@
+import os
+import py.test
+from paste.cgiapp import CGIApplication, CGIError
+from fixture import *
+del setup_module
+
+data_dir = os.path.join(os.path.dirname(__file__), 'cgiapp_data')
+
+
+def test_ok():
+ app = TestApp(CGIApplication('ok.cgi', [data_dir]))
+ res = app.get('')
+ assert res.header('content-type') == 'text/html; charset=UTF-8'
+ assert res.full_status == '200 Okay'
+ assert 'This is the body' in res
+
+def test_form():
+ app = TestApp(CGIApplication('form.cgi', [data_dir]))
+ res = app.post('', params={'name': 'joe'},
+ upload_files=[('up', 'file.txt', 'x'*10000)])
+ assert 'file.txt' in res
+ assert 'joe' in res
+ assert 'x'*10000 in res
+
+def test_error():
+ app = TestApp(CGIApplication('error.cgi', [data_dir]))
+ py.test.raises(CGIError, "app.get('', status=500)")
+
+def test_stderr():
+ app = TestApp(CGIApplication('stderr.cgi', [data_dir]))
+ res = app.get('', expect_errors=True)
+ assert res.status == 500
+ assert 'error' in res
+ assert 'some data' in res.errors
+