Permalink
Browse files

Subreddit CSS: Store subreddit CSS on S3 and minify it.

This means that stylesheets can have all the advantages of other static
files, such as not having session cookies in the request. In addition,
it also means that subreddit objects are drastically smaller in memcache
which saves internal bandwidth and increases cache capacity.
  • Loading branch information...
1 parent bfd2372 commit b2ad131cbbcf78bddc80900bcc9298170dd4ebb6 @spladug spladug committed Sep 19, 2012
Showing with 77 additions and 23 deletions.
  1. +3 −0 r2/example.ini
  2. +12 −2 r2/r2/controllers/front.py
  3. +57 −6 r2/r2/models/subreddit.py
  4. +5 −15 r2/r2/templates/reddit.html
View
@@ -352,6 +352,9 @@ static_secure_domain =
# this is for hosts that don't do on-the-fly gzipping (e.g. s3)
static_pre_gzipped = false
static_secure_pre_gzipped = false
+# which s3 bucket to place subreddit styles on (when empty, stylesheets will be served
+# from the local database instead.
+static_stylesheet_bucket =
# -- translator UI --
# enable/disable access to the translation UI in /admin/i18n
View
@@ -22,6 +22,7 @@
from validator import *
from pylons.i18n import _, ungettext
+from pylons.controllers.util import redirect_to
from reddit_base import RedditController, base_listing, paginated_listing, prevent_framing_and_css
from r2 import config
from r2.models import *
@@ -379,17 +380,26 @@ def GET_newreddit(self, name):
return res
def GET_stylesheet(self):
+ if g.css_killswitch:
+ self.abort404()
+
# de-stale the subreddit object so we don't poison nginx's cache
if not isinstance(c.site, FakeSubreddit):
c.site = Subreddit._byID(c.site._id, data=True, stale=False)
- if hasattr(c.site,'stylesheet_contents') and not g.css_killswitch:
+ if c.site.stylesheet_is_static:
+ # TODO: X-Private-Subreddit?
+ return redirect_to(c.site.stylesheet_url)
+ else:
+ stylesheet_contents = c.site.stylesheet_contents
+
+ if stylesheet_contents:
c.allow_loggedin_cache = True
self.check_modified(c.site,'stylesheet_contents',
private=False, max_age=7*24*60*60,
must_revalidate=False)
c.response_content_type = 'text/css'
- c.response.content = c.site.stylesheet_contents
+ c.response.content = stylesheet_contents
if c.site.type == 'private':
c.response.headers['X-Private-Subreddit'] = 'private'
return c.response
View
@@ -22,6 +22,9 @@
from __future__ import with_statement
+import base64
+import hashlib
+
from pylons import c, g
from pylons.i18n import _
@@ -40,12 +43,13 @@
from r2.models.wiki import WikiPage
from r2.lib.merge import ConflictException
from r2.lib.cache import CL_ONE
+from r2.lib.contrib.rcssmin import cssmin
+from r2.lib import s3cp
import math
from r2.lib.utils import set_last_modified
from r2.models.wiki import WikiPage
-from md5 import md5
import os.path
import random
@@ -59,7 +63,7 @@ class Subreddit(Thing, Printable):
stylesheet = None,
stylesheet_rtl = None,
stylesheet_contents = '',
- stylesheet_hash = '0',
+ stylesheet_hash = '',
firsttext = strings.firsttext,
header = None,
header_size = None,
@@ -212,6 +216,30 @@ def moderators(self):
return self.moderator_ids()
@property
+ def stylesheet_is_static(self):
+ """Is the subreddit using the newer static file based stylesheets?"""
+ return g.static_stylesheet_bucket and len(self.stylesheet_hash) == 27
+
+ static_stylesheet_prefix = "subreddit-stylesheet/"
+
+ @property
+ def static_stylesheet_name(self):
+ return "".join((self.static_stylesheet_prefix,
+ self.stylesheet_hash,
+ ".css"))
+
+ @property
+ def stylesheet_url(self):
+ from r2.lib.template_helpers import static, get_domain
+
+ if self.stylesheet_is_static:
+ return static(self.static_stylesheet_name)
+ else:
+ return "http://%s/stylesheet.css?v=%s" % (get_domain(cname=False,
+ subreddit=True),
+ self.stylesheet_hash)
+
+ @property
def stylesheet_contents_user(self):
try:
return WikiPage.get(self, 'config/stylesheet')._get('content','')
@@ -340,10 +368,33 @@ def change_css(self, content, parsed, prev=None, reason=None, author=None, force
except tdb_cassandra.NotFound:
wiki = WikiPage.create(self, 'config/stylesheet')
wr = wiki.revise(content, previous=prev, author=author, reason=reason, force=force)
- self.stylesheet_contents = parsed
- self.stylesheet_hash = md5(parsed).hexdigest()
- set_last_modified(self, 'stylesheet_contents')
- c.site._commit()
+
+ minified = cssmin(parsed)
+ if minified:
+ if g.static_stylesheet_bucket:
+ digest = hashlib.sha1(minified).digest()
+ self.stylesheet_hash = (base64.urlsafe_b64encode(digest)
+ .rstrip("="))
+
+ s3cp.send_file(g.static_stylesheet_bucket,
+ self.static_stylesheet_name,
+ minified,
+ content_type="text/css",
+ never_expire=True,
+ replace=False,
+ )
+
+ self.stylesheet_contents = ""
+ else:
+ self.stylesheet_hash = hashlib.md5(minified).hexdigest()
+ self.stylesheet_contents = minified
+ set_last_modified(self, 'stylesheet_contents')
+ else:
+ self.stylesheet_contents = ""
+ self.stylesheet_hash = ""
+ self.stylesheet_contents_user = "" # reads from wiki; ensure pg clean
+ self._commit()
+
ModAction.create(self, c.user, action='wikirevise', details='Updated subreddit stylesheet')
return wr
@@ -82,21 +82,11 @@
%endif
%endif
- %if c.can_apply_styles and c.allow_styles and c.site.stylesheet_contents:
- <% inline_stylesheet = (
- len(c.site.stylesheet_contents) < 1024
- and '<' not in c.site.stylesheet_contents) %>
- %if inline_stylesheet:
- ## for very simple stylesheets, we can just include them inline
- <style type="text/css" title="applied_subreddit_stylesheet">
- ${unsafe(c.site.stylesheet_contents)}
- </style>
- %else:
- <link rel="stylesheet"
- href="http://${get_domain(cname=False, subreddit=True)}/stylesheet.css?v=${c.site.stylesheet_hash}"
- title="applied_subreddit_stylesheet"
- type="text/css" />
- %endif
+ %if c.can_apply_styles and c.allow_styles and c.site.stylesheet_hash:
+ <link rel="stylesheet"
+ href="${c.site.stylesheet_url}"
+ title="applied_subreddit_stylesheet"
+ type="text/css">
%endif
%if getattr(thing, "additional_css", None):
<link rel="stylesheet" href="${static(thing.additional_css)}"

0 comments on commit b2ad131

Please sign in to comment.