diff --git a/r2/r2/config/middleware.py b/r2/r2/config/middleware.py index a275dd3e2..d90d5ae63 100644 --- a/r2/r2/config/middleware.py +++ b/r2/r2/config/middleware.py @@ -31,7 +31,7 @@ from paste.cascade import Cascade from paste.registry import RegistryManager from paste.urlparser import StaticURLParser from paste.deploy.converters import asbool -from pylons import config +from pylons import config, response from pylons.error import error_template from pylons.middleware import ErrorDocuments, ErrorHandler, StaticJavascripts from pylons.wsgiapp import PylonsApp, PylonsBaseWSGIApp @@ -86,14 +86,8 @@ def error_mapper(code, message, environ, global_conf=None, **kw): #preserve x-sup-id when 304ing if code == 304: - #check to see if c is useable - try: - c.test - except TypeError: - pass - else: - if c.response.headers.has_key('x-sup-id'): - d['x-sup-id'] = c.response.headers['x-sup-id'] + if response.headers.has_key('x-sup-id'): + d['x-sup-id'] = response.headers['x-sup-id'] extension = environ.get("extension") if extension: diff --git a/r2/r2/controllers/buttons.py b/r2/r2/controllers/buttons.py index b8111b8c1..ab82d6317 100644 --- a/r2/r2/controllers/buttons.py +++ b/r2/r2/controllers/buttons.py @@ -25,9 +25,9 @@ from r2.lib.pages import (ButtonLite, ButtonDemoPanel, WidgetDemoPanel, Bookmarklets, BoringPage) from r2.lib.pages.things import wrap_links from r2.models import * -from r2.lib.utils import tup +from r2.lib.utils import tup, to_js from r2.lib.validator import * -from pylons import c, request +from pylons import c, request, response from pylons.i18n import _ class ButtonsController(RedditController): @@ -81,7 +81,6 @@ class ButtonsController(RedditController): styled = VBoolean('styled', default=True)) def GET_button_lite(self, buttonimage, title, url, styled, newwindow): c.render_style = 'js' - c.response_content_type = 'text/javascript; charset=UTF-8' if not url: url = request.referer @@ -99,7 +98,8 @@ class ButtonsController(RedditController): styled = styled, **kw) bjs = self.get_wrapped_link(url, wrapper = builder_wrapper) - return self.sendjs(bjs.render(), callback='', escape=False) + response.content_type = "text/javascript" + return to_js(bjs.render(), callback='', escape=False) def GET_button_demo_page(self): # no buttons for domain listings -> redirect to top level diff --git a/r2/r2/controllers/captcha.py b/r2/r2/controllers/captcha.py index da0136a1e..e552d0b59 100644 --- a/r2/r2/controllers/captcha.py +++ b/r2/r2/controllers/captcha.py @@ -23,12 +23,13 @@ from reddit_base import RedditController import StringIO import r2.lib.captcha as captcha -from pylons import c +from pylons import c, response class CaptchaController(RedditController): def GET_captchaimg(self, iden): image = captcha.get_image(iden) f = StringIO.StringIO() image.save(f, "PNG") - return self.sendpng(f.getvalue()) + response.content_type = "image/png;" + return f.getvalue() diff --git a/r2/r2/controllers/error.py b/r2/r2/controllers/error.py index 6b3c22f4c..3095950b2 100644 --- a/r2/r2/controllers/error.py +++ b/r2/r2/controllers/error.py @@ -112,7 +112,6 @@ class ErrorController(RedditController): def send403(self): - c.response.status_code = 403 c.site = DefaultSR() if 'usable_error_content' in request.environ: return request.environ['usable_error_content'] @@ -124,15 +123,12 @@ class ErrorController(RedditController): return res.render() def send404(self): - c.response.status_code = 404 if 'usable_error_content' in request.environ: return request.environ['usable_error_content'] return pages.RedditError(_("page not found"), _("the page you requested does not exist")).render() def send429(self): - c.response.status_code = 429 - retry_after = request.environ.get("retry_after") if retry_after: response.headers["Retry-After"] = str(retry_after) @@ -145,8 +141,7 @@ class ErrorController(RedditController): return template.render(logo_url=static(g.default_header_url)) def send503(self): - c.response.status_code = 503 - c.response.headers['Retry-After'] = request.environ['retry_after'] + response.headers["Retry-After"] = str(request.environ["retry_after"]) return request.environ['usable_error_content'] def GET_document(self): @@ -167,13 +162,12 @@ class ErrorController(RedditController): c.site = Subreddit._by_name(srname) if c.render_style not in self.allowed_render_styles: if code not in (204, 304): - c.response.content = str(code) - c.response.status_code = code - return c.response + return str(code) + else: + return "" elif c.render_style in extensions.API_TYPES: data = request.environ.get('extra_error_data', {'error': code}) - c.response.content = websafe_json(json.dumps(data)) - return c.response + return websafe_json(json.dumps(data)) elif takedown and code == 404: link = Link._by_fullname(takedown) return pages.TakedownPage(link).render() @@ -192,8 +186,8 @@ class ErrorController(RedditController): if request.GET.has_key('x-sup-id'): x_sup_id = request.GET.get('x-sup-id') if '\r\n' not in x_sup_id: - c.response.headers['x-sup-id'] = x_sup_id - return c.response + response.headers['x-sup-id'] = x_sup_id + return "" elif c.site: return self.send404() else: diff --git a/r2/r2/controllers/front.py b/r2/r2/controllers/front.py index ddc1bf6fc..3d94387aa 100755 --- a/r2/r2/controllers/front.py +++ b/r2/r2/controllers/front.py @@ -49,7 +49,7 @@ from r2.lib.errors import errors from listingcontroller import ListingController from oauth2 import OAuth2ResourceController, require_oauth2_scope from api_docs import api_doc, api_section -from pylons import c, request, request +from pylons import c, request, response from r2.models.token import EmailVerificationToken from r2.controllers.ipn import generate_blob @@ -410,11 +410,10 @@ class FrontController(RedditController, OAuth2ResourceController): must_revalidate=False, ) - c.response_content_type = 'text/css' - c.response.content = stylesheet_contents + response.content_type = 'text/css' if c.site.type == 'private': - c.response.headers['X-Private-Subreddit'] = 'private' - return c.response + response.headers['X-Private-Subreddit'] = 'private' + return stylesheet_contents else: return self.abort404() @@ -979,8 +978,7 @@ class FrontController(RedditController, OAuth2ResourceController): sup.set_expires_header() if c.extension == 'json': - c.response.content = sup.sup_json(period) - return c.response + return sup.sup_json(period) else: return self.abort404() @@ -991,8 +989,7 @@ class FrontController(RedditController, OAuth2ResourceController): def GET_traffic(self, article): content = trafficpages.PromotedLinkTraffic(article) if c.render_style == 'csv': - c.response.content = content.as_csv() - return c.response + return content.as_csv() return LinkInfoPage(link=article, page_classes=["promoted-traffic"], @@ -1005,8 +1002,7 @@ class FrontController(RedditController, OAuth2ResourceController): if link: content = trafficpages.PromoTraffic(link) if c.render_style == 'csv': - c.response.content = content.as_csv() - return c.response + return content.as_csv() return LinkInfoPage(link=link, page_classes=["promo-traffic"], comment=None, @@ -1237,14 +1233,13 @@ class FormsController(RedditController): def GET_validuser(self): """checks login cookie to verify that a user is logged in and returns their user name""" - c.response_content_type = 'text/plain' + response.content_type = 'text/plain' if c.user_is_loggedin: # Change cookie based on can_wiki trac permissions perm = str(c.user.can_wiki(default=False)) - c.response.content = c.user.name + "," + perm + return c.user.name + "," + perm else: - c.response.content = '' - return c.response + return "" def _render_opt_in_out(self, msg_hash, leave): """Generates the form for an optin/optout page""" diff --git a/r2/r2/controllers/mediaembed.py b/r2/r2/controllers/mediaembed.py index 62c0f7b4b..8f2b4a833 100644 --- a/r2/r2/controllers/mediaembed.py +++ b/r2/r2/controllers/mediaembed.py @@ -56,7 +56,7 @@ class MediaembedController(MinimalController): class AdController(MinimalController): def request_key(self): - return make_key('request_key', + return make_key('request_', c.lang, c.content_langs, request.host, diff --git a/r2/r2/controllers/post.py b/r2/r2/controllers/post.py index 36dbb40ef..40a361b3a 100644 --- a/r2/r2/controllers/post.py +++ b/r2/r2/controllers/post.py @@ -32,9 +32,6 @@ from r2.models import * import hashlib class PostController(ApiController): - def api_wrapper(self, kw): - return Storage(**kw) - def set_options(self, all_langs, pref_lang, **kw): if c.errors.errors: print "fucker" @@ -180,7 +177,7 @@ class PostController(ApiController): def POST_login(self, dest, *a, **kw): ApiController._handle_login(self, *a, **kw) c.render_style = "html" - c.response_content_type = "" + response.content_type = "text/html" if c.errors: return LoginPage(user_login = request.post.get('user'), @@ -192,7 +189,7 @@ class PostController(ApiController): def POST_reg(self, dest, *a, **kw): ApiController._handle_register(self, *a, **kw) c.render_style = "html" - c.response_content_type = "" + response.content_type = "text/html" if c.errors: return LoginPage(user_reg = request.post.get('user'), diff --git a/r2/r2/controllers/promotecontroller.py b/r2/r2/controllers/promotecontroller.py index bf2153ce2..51d1f6372 100644 --- a/r2/r2/controllers/promotecontroller.py +++ b/r2/r2/controllers/promotecontroller.py @@ -184,11 +184,9 @@ class PromoteController(ListingController): def GET_admingraph(self): content = Promote_Graph(admin_view=True) if c.render_style == 'csv': - c.response.content = content.as_csv() - return c.response + return content.as_csv() return PromotePage("admingraph", content=content).render() - def GET_inventory(self, sr_name): ''' Return available inventory data as json for use in ajax calls diff --git a/r2/r2/controllers/reddit_base.py b/r2/r2/controllers/reddit_base.py index ef53d1726..e43ba2a3c 100644 --- a/r2/r2/controllers/reddit_base.py +++ b/r2/r2/controllers/reddit_base.py @@ -53,6 +53,7 @@ from r2.lib.errors import ( ForbiddenError, errors, ) +from r2.lib.filters import _force_utf8 from r2.lib.strings import strings from r2.lib.template_helpers import add_sr from r2.lib.tracking import encrypt, decrypt @@ -63,6 +64,7 @@ from r2.lib.utils import ( http_utils, is_subdomain, is_throttled, + tup, ) from r2.lib.validator import ( build_arg_list, @@ -368,7 +370,7 @@ def set_subreddit(): def set_content_type(): e = request.environ c.render_style = e['render_style'] - c.response_content_type = e['content_type'] + response.content_type = e['content_type'] if e.has_key('extension'): c.extension = ext = e['extension'] @@ -376,7 +378,7 @@ def set_content_type(): def to_js(content): return utils.to_js(content, callback=request.params.get( "callback", "document.write")) - c.response_wrappers.append(to_js) + c.response_wrapper = to_js if ext in ("rss", "api", "json") and request.method.upper() == "GET": user = valid_feed(request.GET.get("user"), request.GET.get("feed"), @@ -591,10 +593,9 @@ def cross_domain(origin_check=is_trusted_origin, **options): if cors_perms["origin_check"](g.origin): name = request.environ["pylons.routes_dict"]["action_name"] resp = fn(self, *args, **kwargs) - c.cookies.add('hoist_%s' % name, ''.join(resp.content)) - c.response_content_type = 'text/html' - resp.content = '' - return resp + c.cookies.add('hoist_%s' % name, ''.join(tup(resp))) + response.content_type = 'text/html' + return "" else: abort(403) else: @@ -638,7 +639,7 @@ class MinimalController(BaseController): except CookieError: cookies_key = '' - return make_key('request_key_', + return make_key('request_', c.lang, c.content_langs, request.host, @@ -652,7 +653,7 @@ class MinimalController(BaseController): cookies_key) def cached_response(self): - return c.response + return response.content def pre(self): action = request.environ["pylons.routes_dict"].get("action") @@ -661,7 +662,7 @@ class MinimalController(BaseController): else: c.request_timer = SimpleSillyStub() - c.response_wrappers = [] + c.response_wrapper = None c.start_time = datetime.now(g.tz) c.request_timer.start() g.reset_caches() @@ -692,7 +693,6 @@ class MinimalController(BaseController): r = g.pagecache.get(self.request_key()) if r: r, c.cookies = r - response = c.response response.headers = r.headers response.content = r.content @@ -712,24 +712,19 @@ class MinimalController(BaseController): c.request_timer.name = request_timer_name("cached_response") # make sure to carry over the content type - c.response_content_type = r.headers['content-type'] + response.content_type = r.headers['content-type'] c.used_cache = True # response wrappers have already been applied before cache write - c.response_wrappers = [] - + c.response_wrapper = None def post(self): c.request_timer.intermediate("action") - response = c.response - content = filter(None, response.content) - if isinstance(content, (list, tuple)): - content = ''.join(content) - for w in c.response_wrappers: - content = w(content) - response.content = content - if c.response_content_type: - response.headers['Content-Type'] = c.response_content_type + if c.response_wrapper: + content = "".join(_force_utf8(x) + for x in tup(response.content) if x) + wrapped_content = c.response_wrapper(content) + response.content = wrapped_content if c.user_is_loggedin and not c.allow_loggedin_cache: response.headers['Cache-Control'] = 'no-cache' @@ -738,18 +733,16 @@ class MinimalController(BaseController): if c.deny_frames: response.headers["X-Frame-Options"] = "DENY" - #return #set content cache if (g.page_cache_time and request.method.upper() == 'GET' and (not c.user_is_loggedin or c.allow_loggedin_cache) and not c.used_cache - and response.status_code not in (429, 503) - and response.content and response.content[0]): + and response.status_code not in (429, 503)): try: g.pagecache.set(self.request_key(), - (response, c.cookies), - g.page_cache_time) + (response._current_obj(), c.cookies), + g.page_cache_time) except MemcachedError as e: # this codepath will actually never be hit as long as # the pagecache memcached client is in no_reply mode. @@ -819,11 +812,6 @@ class MinimalController(BaseController): """Return empty responses for CORS preflight requests""" self.check_cors() - def sendpng(self, string): - c.response_content_type = 'image/png' - c.response.content = string - return c.response - def update_qstring(self, dict): merged = copy(request.get) merged.update(dict) @@ -831,18 +819,7 @@ class MinimalController(BaseController): def api_wrapper(self, kw): data = simplejson.dumps(kw) - c.response.content = filters.websafe_json(data) - return c.response - - def iframe_api_wrapper(self, kw): - data = simplejson.dumps(kw) - c.response_content_type = 'text/html' - c.response.content = ( - '') % filters.websafe_json(data) - return c.response + return filters.websafe_json(data) class RedditController(MinimalController): @@ -1036,7 +1013,7 @@ class RedditController(MinimalController): last_modified = last_modified.replace(microsecond=0) date_str = http_utils.http_date_str(last_modified) - c.response.headers['last-modified'] = date_str + response.headers['last-modified'] = date_str cache_control = [] if private: @@ -1044,7 +1021,7 @@ class RedditController(MinimalController): cache_control.append('max-age=%d' % max_age.total_seconds()) if must_revalidate: cache_control.append('must-revalidate') - c.response.headers['cache-control'] = ', '.join(cache_control) + response.headers['cache-control'] = ', '.join(cache_control) modified_since = request.if_modified_since if modified_since and modified_since >= last_modified: diff --git a/r2/r2/lib/base.py b/r2/r2/lib/base.py index 880e595c8..0f4b951c1 100644 --- a/r2/r2/lib/base.py +++ b/r2/r2/lib/base.py @@ -24,7 +24,7 @@ import _pylibmc import pycassa.pool import sqlalchemy.exc -from pylons import Response, c, g, request, session, config +from pylons import c, g, request, session, config, response from pylons.controllers import WSGIController, Controller from pylons.i18n import N_, _, ungettext, get_lang from paste import httpexceptions @@ -141,7 +141,6 @@ class BaseController(WSGIController): request.environ['pylons.routes_dict']['action_name'] = action request.environ['pylons.routes_dict']['action'] = handler_name - c.response = Response() try: res = WSGIController.__call__(self, environ, start_response) except Exception as e: @@ -221,14 +220,8 @@ class BaseController(WSGIController): sends the user to that location with the provided HTTP code. """ dest = cls.format_output_url(dest or "/") - c.response.headers['Location'] = dest - c.response.status_code = code - return c.response - - def sendjs(self,js, callback="document.write", escape=True): - c.response.headers['Content-Type'] = 'text/javascript' - c.response.content = to_js(js, callback, escape) - return c.response + response.status_code = code + response.headers['Location'] = dest class EmbedHandler(urllib2.BaseHandler, urllib2.HTTPHandler, urllib2.HTTPErrorProcessor, urllib2.HTTPDefaultErrorHandler): diff --git a/r2/r2/lib/jsonresponse.py b/r2/r2/lib/jsonresponse.py index abffa43ef..bc86ff53e 100644 --- a/r2/r2/lib/jsonresponse.py +++ b/r2/r2/lib/jsonresponse.py @@ -40,7 +40,7 @@ class JsonResponse(object): api func. """ - content_type = 'application/json; charset=UTF-8' + content_type = 'application/json' def __init__(self): self._clear() diff --git a/r2/r2/lib/pages/pages.py b/r2/r2/lib/pages/pages.py index b16919e7f..af322150e 100755 --- a/r2/r2/lib/pages/pages.py +++ b/r2/r2/lib/pages/pages.py @@ -93,8 +93,7 @@ def responsive(res, space_compress = False): res = "%s(%s)" % (websafe_json(c.allowed_callback), res) elif space_compress: res = spaceCompress(res) - c.response.content = res - return c.response + return res class Reddit(Templated): '''Base class for rendering a page on reddit. Handles toolbar creation, diff --git a/r2/r2/lib/sup.py b/r2/r2/lib/sup.py index 2230ade15..50efe2e3c 100644 --- a/r2/r2/lib/sup.py +++ b/r2/r2/lib/sup.py @@ -30,7 +30,7 @@ import simplejson from r2.lib.utils import rfc3339_date_str, http_date_str, to36 from r2.lib.memoize import memoize from r2.lib.template_helpers import get_domain -from pylons import g, c +from pylons import g, c, response PERIODS = [600, 300, 60] MIN_PERIOD = min(PERIODS) @@ -104,10 +104,10 @@ def sup_json(period): def set_sup_header(user, action): sup_id = make_sup_id(user, action) - c.response.headers['x-sup-id'] = sup_url() + '#' + sup_id + response.headers['x-sup-id'] = sup_url() + '#' + sup_id def set_expires_header(): seconds = make_cur_time(MIN_PERIOD) + MIN_PERIOD expire_time = datetime.fromtimestamp(seconds, g.tz) - c.response.headers['expires'] = http_date_str(expire_time) + response.headers['expires'] = http_date_str(expire_time) diff --git a/r2/r2/lib/validator/validator.py b/r2/r2/lib/validator/validator.py index 55ffb817c..819afe90c 100644 --- a/r2/r2/lib/validator/validator.py +++ b/r2/r2/lib/validator/validator.py @@ -213,7 +213,7 @@ def api_validate(response_type=None): else: responder = JsonResponse() - c.response_content_type = responder.content_type + response.content_type = responder.content_type try: kw = _make_validated_kw(fn, simple_vals, param_vals, env) @@ -291,7 +291,12 @@ def validatedMultipartForm(self, self_method, responder, simple_vals, if val: return val else: - return self.iframe_api_wrapper(responder.make_response()) + data = simplejson.dumps(responder.make_response()) + response.content_type = "text/html" + return ('') % filters.websafe_json(data) return _validatedForm(self, wrapped_self_method, responder, simple_vals, param_vals, *a, **kw)