@@ -39,9 +39,6 @@ use crate::{
39
39
40
40
mod session;
41
41
42
- const MAX_CONTENT_LENGTH : u64 = 1024 * 100 ;
43
- const CAPACITY : usize = 100 ;
44
-
45
42
// n.b. Because OPTIONS requests are handled by the Python code, we don't need to set Access-Control-Allow-Headers.
46
43
fn prepare_headers ( headers : & mut HeaderMap , session : & Session ) {
47
44
headers. typed_insert ( AccessControlAllowOrigin :: ANY ) ;
@@ -53,38 +50,42 @@ fn prepare_headers(headers: &mut HeaderMap, session: &Session) {
53
50
headers. typed_insert ( session. last_modified ( ) ) ;
54
51
}
55
52
56
- fn check_input_headers ( headers : & HeaderMap ) -> PyResult < Mime > {
57
- let ContentLength ( content_length) = headers. typed_get_required ( ) ?;
58
-
59
- if content_length > MAX_CONTENT_LENGTH {
60
- return Err ( SynapseError :: new (
61
- StatusCode :: PAYLOAD_TOO_LARGE ,
62
- "Payload too large" . to_owned ( ) ,
63
- "M_TOO_LARGE" ,
64
- None ,
65
- None ,
66
- ) ) ;
67
- }
68
-
69
- let content_type: ContentType = headers. typed_get_required ( ) ?;
70
-
71
- Ok ( content_type. into ( ) )
72
- }
73
-
74
53
#[ pyclass]
75
54
struct RendezvousHandler {
76
55
base : Uri ,
77
56
clock : PyObject ,
78
57
sessions : BTreeMap < Ulid , Session > ,
58
+ capacity : usize ,
59
+ max_content_length : u64 ,
79
60
}
80
61
81
62
impl RendezvousHandler {
82
- fn evict ( & mut self , now : SystemTime , max_entries : usize ) {
63
+ /// Check the input headers of a request which sets data for a session, and return the content type.
64
+ fn check_input_headers ( & self , headers : & HeaderMap ) -> PyResult < Mime > {
65
+ let ContentLength ( content_length) = headers. typed_get_required ( ) ?;
66
+
67
+ if content_length > self . max_content_length {
68
+ return Err ( SynapseError :: new (
69
+ StatusCode :: PAYLOAD_TOO_LARGE ,
70
+ "Payload too large" . to_owned ( ) ,
71
+ "M_TOO_LARGE" ,
72
+ None ,
73
+ None ,
74
+ ) ) ;
75
+ }
76
+
77
+ let content_type: ContentType = headers. typed_get_required ( ) ?;
78
+
79
+ Ok ( content_type. into ( ) )
80
+ }
81
+
82
+ /// Evict expired sessions and remove the oldest sessions until we're under the capacity.
83
+ fn evict ( & mut self , now : SystemTime ) {
83
84
// First remove all the entries which expired
84
85
self . sessions . retain ( |_, session| !session. expired ( now) ) ;
85
86
86
87
// Then we remove the oldest entires until we're under the limit
87
- while self . sessions . len ( ) > max_entries {
88
+ while self . sessions . len ( ) > self . capacity {
88
89
self . sessions . pop_first ( ) ;
89
90
}
90
91
}
@@ -93,7 +94,14 @@ impl RendezvousHandler {
93
94
#[ pymethods]
94
95
impl RendezvousHandler {
95
96
#[ new]
96
- fn new ( py : Python < ' _ > , homeserver : & PyAny ) -> PyResult < Py < Self > > {
97
+ #[ pyo3( signature = ( homeserver, /, capacity=100 , max_content_length=1024 * 1024 , eviction_interval=60 * 1000 ) ) ]
98
+ fn new (
99
+ py : Python < ' _ > ,
100
+ homeserver : & PyAny ,
101
+ capacity : usize ,
102
+ max_content_length : u64 ,
103
+ eviction_interval : u64 ,
104
+ ) -> PyResult < Py < Self > > {
97
105
let base: String = homeserver
98
106
. getattr ( "config" ) ?
99
107
. getattr ( "server" ) ?
@@ -112,13 +120,17 @@ impl RendezvousHandler {
112
120
base,
113
121
clock,
114
122
sessions : BTreeMap :: new ( ) ,
123
+ capacity,
124
+ max_content_length,
115
125
} ,
116
126
) ?;
117
127
118
128
let evict = self_. getattr ( py, "_evict" ) ?;
119
- homeserver
120
- . call_method0 ( "get_clock" ) ?
121
- . call_method ( "looping_call" , ( evict, 500 ) , None ) ?;
129
+ homeserver. call_method0 ( "get_clock" ) ?. call_method (
130
+ "looping_call" ,
131
+ ( evict, eviction_interval) ,
132
+ None ,
133
+ ) ?;
122
134
123
135
Ok ( self_)
124
136
}
@@ -127,23 +139,23 @@ impl RendezvousHandler {
127
139
let clock = self . clock . as_ref ( py) ;
128
140
let now: u64 = clock. call_method0 ( "time_msec" ) ?. extract ( ) ?;
129
141
let now = SystemTime :: UNIX_EPOCH + Duration :: from_millis ( now) ;
130
- self . evict ( now, CAPACITY ) ;
142
+ self . evict ( now) ;
131
143
132
144
Ok ( ( ) )
133
145
}
134
146
135
147
fn handle_post ( & mut self , py : Python < ' _ > , twisted_request : & PyAny ) -> PyResult < ( ) > {
136
148
let request = http_request_from_twisted ( twisted_request) ?;
137
149
138
- let content_type = check_input_headers ( request. headers ( ) ) ?;
150
+ let content_type = self . check_input_headers ( request. headers ( ) ) ?;
139
151
140
152
let clock = self . clock . as_ref ( py) ;
141
153
let now: u64 = clock. call_method0 ( "time_msec" ) ?. extract ( ) ?;
142
154
let now = SystemTime :: UNIX_EPOCH + Duration :: from_millis ( now) ;
143
155
144
156
// We trigger an immediate eviction if we're at 2x the capacity
145
- if self . sessions . len ( ) >= CAPACITY * 2 {
146
- self . evict ( now, CAPACITY ) ;
157
+ if self . sessions . len ( ) >= self . capacity * 2 {
158
+ self . evict ( now) ;
147
159
}
148
160
149
161
// Generate a new ULID for the session from the current time.
@@ -210,7 +222,7 @@ impl RendezvousHandler {
210
222
fn handle_put ( & mut self , py : Python < ' _ > , twisted_request : & PyAny , id : & str ) -> PyResult < ( ) > {
211
223
let request = http_request_from_twisted ( twisted_request) ?;
212
224
213
- let content_type = check_input_headers ( request. headers ( ) ) ?;
225
+ let content_type = self . check_input_headers ( request. headers ( ) ) ?;
214
226
215
227
let if_match: IfMatch = request. headers ( ) . typed_get_required ( ) ?;
216
228
0 commit comments