Added basic in-memory REST transaction storage. Only the latest transaction for a given path/access_token combo is stored in order to prevent storing ALL request/response pairs.

This commit is contained in:
Kegan Dougal 2014-08-26 14:13:32 +01:00
parent 7d79021c42
commit 732d954f89
3 changed files with 103 additions and 2 deletions

View File

@ -15,6 +15,7 @@
""" This module contains base REST classes for constructing REST servlets. """
from synapse.api.urls import CLIENT_PREFIX
from synapse.rest.transactions import HttpTransactionStore
import re
@ -59,6 +60,7 @@ class RestServlet(object):
self.handlers = hs.get_handlers()
self.event_factory = hs.get_event_factory()
self.auth = hs.get_auth()
self.txns = HttpTransactionStore()
def register(self, http_server):
""" Register this servlet with the given HTTP server. """

View File

@ -375,10 +375,16 @@ class RoomMembershipRestServlet(RestServlet):
register_txn_path(self, PATTERN, http_server)
def on_POST(self, request, room_id, membership_action):
return (200, "Not implemented")
return (200, "POST Not implemented")
def on_PUT(self, request, room_id, membership_action, txn_id):
return (200, "Not implemented")
(code, response) = self.txns.get_client_transaction(request, txn_id)
if code:
return (code, response)
response = (200, "PUT not implemented txnid %s" % txn_id)
self.txns.store_client_transaction(request, txn_id, response)
return response
def _parse_json(request):

View File

@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
# Copyright 2014 matrix.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""This module contains logic for storing HTTP PUT transactions. This is used
to ensure idempotency when performing PUTs using the REST API."""
import logging
logger = logging.getLogger(__name__)
class HttpTransactionStore(object):
def __init__(self):
# { key : (txn_id, response) }
self.transactions = {}
def get_response(self, key, txn_id):
"""Retrieve a response for this request.
Args:
key (str): A transaction-independent key for this request. Typically
this is a combination of the path (without the transaction id) and
the user's access token.
txn_id (str): The transaction ID for this request
Returns:
A tuple of (HTTP response code, response content) or None.
"""
try:
logger.debug("get_response Key: %s TxnId: %s", key, txn_id)
(last_txn_id, response) = self.transactions[key]
if txn_id == last_txn_id:
return response
except KeyError:
pass
return None
def store_response(self, key, txn_id, response):
"""Stores an HTTP response tuple.
Args:
key (str): A transaction-independent key for this request. Typically
this is a combination of the path (without the transaction id) and
the user's access token.
txn_id (str): The transaction ID for this request.
response (tuple): A tuple of (HTTP response code, response content)
"""
logger.debug("store_response Key: %s TxnId: %s", key, txn_id)
self.transactions[key] = (txn_id, response)
def store_client_transaction(self, request, txn_id, response):
"""Stores the request/response pair of an HTTP transaction.
Args:
request (twisted.web.http.Request): The twisted HTTP request. This
request must have the transaction ID as the last path segment.
response (tuple): A tuple of (response code, response dict)
txn_id (str): The transaction ID for this request.
"""
self.store_response(self._get_key(request), txn_id, response)
def get_client_transaction(self, request, txn_id):
"""Retrieves a stored response if there was one.
Args:
request (twisted.web.http.Request): The twisted HTTP request. This
request must have the transaction ID as the last path segment.
txn_id (str): The transaction ID for this request.
Returns:
The response tuple or (None, None).
"""
response = self.get_response(self._get_key(request), txn_id)
if response is None:
return (None, None)
return response
def _get_key(self, request):
token = request.args["access_token"][0]
path_without_txn_id = request.path.rsplit("/", 1)[0]
return path_without_txn_id + "/" + token