Commits

Barry Schwartz  committed dfda50b

Initial commit.

  • Participants

Comments (0)

Files changed (2)

+AddHandler fcgid-script .fcgi
+
+RewriteEngine On
+
+RewriteBase /
+
+RewriteRule ^$ index.html [QSA]
+RewriteRule ^([^.]+)$ $1.html [QSA]
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
+ErrorDocument 500 "<h2>Application error</h2>Application failed to start properly"

File dispatch.fcgi

+#!/home2/crudfact/py2.4/bin/python
+
+# Copyright (c) 2010 Barry Schwartz
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+# 
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+# This Python script is based in part on the Ruby program "Fontue"
+# (http://fontue.com), which is
+#
+#    Copyright (c) 2010 Garrick Van Buren, Working Pathways, Inc.
+
+#--------------------------------------------------------------------------
+
+import os
+import re
+import stat
+
+from email.utils import formatdate
+from flup.server.fcgi_fork import WSGIServer
+
+#--------------------------------------------------------------------------
+
+font_server_directory = '/home2/crudfact/wsgi_apps/fontserver/fonts'
+
+woff_re = re.compile('Firefox/[3-9]|Konqueror/[4-9]')
+eot_re = re.compile('\sMSIE\s')
+gzip_re = re.compile('(^\s*gzip\s*$|\s*gzip\s*,|,\s*gzip\s*$|,\s*gzip\s*,)')
+font_name_re = re.compile('^[0-9a-zA-Z_-]+$')
+
+def likes_woff(user_agent):
+    return woff_re.search(user_agent) is not None
+
+def likes_eot(user_agent):
+    return eot_re.search(user_agent) is not None
+
+def likes_gzip(environ):
+    try:
+        acceptable_encodings = environ['HTTP_ACCEPT_ENCODING']
+        gzip_is_acceptable = (gzip_re.search(environ['HTTP_ACCEPT_ENCODING']) is not None)
+    except:
+        gzip_is_acceptable = False
+    return gzip_is_acceptable
+
+def font_file(font_name, extension):
+    return os.path.join(font_server_directory, font_name + extension)
+
+def font_file_exists(font_name, extension):
+    font = font_file(font_name, extension)
+    return os.path.isfile(font)
+
+def feed_file_data(f, buffer_size = 8192):
+    s = f.read(buffer_size)
+    while s != '':
+        yield s
+        s = f.read(buffer_size)
+    f.close()
+
+def serve_font(environ, start_response):
+
+    if 'HTTP_USER_AGENT' not in environ:
+        start_response('404 NOT FOUND', [('Content-Type', 'text/html')])
+        return ['<p>404 NOT FOUND<hr/>Font service requires HTTP_USER_AGENT, which is missing</p>']
+
+    user_agent = environ['HTTP_USER_AGENT']
+
+    if 'PATH_INFO' not in environ or len(environ['PATH_INFO']) < 2 or environ['PATH_INFO'][0] != '/':
+        start_response('404 NOT FOUND', [('Content-Type', 'text/html')])
+        return ['<p>404 NOT FOUND<hr/>Incompatible PATH_INFO</p>']
+
+    font_name = environ['PATH_INFO'].split('/')[1]
+
+    if font_name_re.search(font_name) is None:
+        start_response('404 NOT FOUND', [('Content-Type', 'text/html')])
+        return ['<p>404 NOT FOUND<hr/>Font name contains unsupported characters</p>']
+
+    gzippable = likes_gzip(environ)
+
+    # TODO: Support SVG fonts, with content type image/svg+xml.
+
+    if likes_woff(user_agent) and font_file_exists(font_name, '.woff'):
+        font = font_file(font_name, '.woff')
+        content_type = 'application/octet-stream'
+        encoding = None
+
+    elif likes_eot(user_agent) and gzippable and font_file_exists(font_name, '.eot.jgz'):
+        font = font_file(font_name, '.eot.jgz')
+        content_type = 'application/octet-stream'
+        encoding = 'gzip'
+
+    elif likes_eot(user_agent) and font_file_exists(font_name, '.eot'):
+        font = font_file(font_name, '.eot')
+        content_type = 'application/octet-stream'
+        encoding = None
+
+    elif gzippable and font_file_exists(font_name, '.ttf.jgz'):
+        font = font_file(font_name, '.ttf.jgz')
+        content_type = 'application/octet-stream'
+        encoding = 'gzip'
+
+    elif font_file_exists(font_name, '.ttf'):
+        font = font_file(font_name, '.ttf')
+        content_type = 'application/octet-stream'
+        encoding = None
+
+    elif gzippable and font_file_exists(font_name, '.otf.jgz'):
+        font = font_file(font_name, '.otf.jgz')
+        content_type = 'application/octet-stream'
+        encoding = 'gzip'
+
+    elif font_file_exists(font_name, '.otf'):
+        font = font_file(font_name, '.otf')
+        content_type = 'application/octet-stream'
+        encoding = None
+
+    else:
+        start_response('404 NOT FOUND', [('Content-Type', 'text/html')])
+        return ['<p>404 NOT FOUND<hr/>Font not found</p>']
+
+    stat_buf = os.stat(font)
+    font_size = stat_buf[stat.ST_SIZE]
+    mtime = formatdate(stat_buf[stat.ST_MTIME], usegmt = True)
+    now = formatdate(usegmt = True)
+    etag = str(stat_buf[stat.ST_MTIME]) + ':' + str(font_size)
+    one_year = 60 * 60 * 24 * 365
+
+    f = open(font, 'rb')
+    if not f:
+        start_response('404 NOT FOUND', [('Content-Type', 'text/html')])
+        return ['<p>404 NOT FOUND<hr/>Font cannot be opened for reading</p>']
+
+    if 'HTTP_IF_NONE_MATCH' in environ and etag == environ['HTTP_IF_NONE_MATCH']:
+
+        headers = []
+        headers.append(('Date', now))
+        start_response('304 NOT MODIFIED', headers)
+        return []
+
+    else:
+        headers = []
+        headers.append(('Date', now))
+        headers.append(('Last-Modified', mtime))
+        headers.append(('Content-Type', content_type))
+        headers.append(('Content-Length', str(font_size)))
+        if encoding is not None:
+            headers.append(('Content-Encoding', encoding))
+        headers.append(('Content-Disposition', 'inline;filename=' + os.path.basename(font)))
+        headers.append(('Cache-Control', 'max-age=' + str(one_year) + ',public'))
+        headers.append(('Access-Control-Allow-Origin', '*'))
+        headers.append(('ETag', etag))
+        start_response('200 OK', headers)
+        return feed_file_data(f)
+
+#--------------------------------------------------------------------------
+
+WSGIServer(serve_font).run()
+
+#--------------------------------------------------------------------------