*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see .
*
*/
namespace Test\AppFramework\Http;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Utility\ITimeFactory;
class ResponseTest extends \Test\TestCase {
/**
* @var \OCP\AppFramework\Http\Response
*/
private $childResponse;
protected function setUp(): void {
parent::setUp();
$this->childResponse = new Response();
}
public function testAddHeader() {
$this->childResponse->addHeader(' hello ', 'world');
$headers = $this->childResponse->getHeaders();
$this->assertEquals('world', $headers['hello']);
}
public function testSetHeaders() {
$expected = [
'Last-Modified' => 1,
'ETag' => 3,
'Something-Else' => 'hi',
'X-Robots-Tag' => 'none',
];
$this->childResponse->setHeaders($expected);
$headers = $this->childResponse->getHeaders();
$expected['Content-Security-Policy'] = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
$expected['Feature-Policy'] = "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'";
$this->assertEquals($expected, $headers);
}
public function testOverwriteCsp() {
$expected = [
'Content-Security-Policy' => "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' 'unsafe-inline';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self' data:;connect-src 'self';media-src 'self'",
];
$policy = new Http\ContentSecurityPolicy();
$policy->allowInlineScript(true);
$this->childResponse->setContentSecurityPolicy($policy);
$headers = $this->childResponse->getHeaders();
$this->assertEquals(array_merge($expected, $headers), $headers);
}
public function testGetCsp() {
$policy = new Http\ContentSecurityPolicy();
$policy->allowInlineScript(true);
$this->childResponse->setContentSecurityPolicy($policy);
$this->assertEquals($policy, $this->childResponse->getContentSecurityPolicy());
}
public function testGetCspEmpty() {
$this->assertEquals(new Http\EmptyContentSecurityPolicy(), $this->childResponse->getContentSecurityPolicy());
}
public function testAddHeaderValueNullDeletesIt() {
$this->childResponse->addHeader('hello', 'world');
$this->childResponse->addHeader('hello', null);
$this->assertEquals(5, count($this->childResponse->getHeaders()));
}
public function testCacheHeadersAreDisabledByDefault() {
$headers = $this->childResponse->getHeaders();
$this->assertEquals('no-cache, no-store, must-revalidate', $headers['Cache-Control']);
}
public function testAddCookie() {
$this->childResponse->addCookie('foo', 'bar');
$this->childResponse->addCookie('bar', 'foo', new \DateTime('1970-01-01'));
$expectedResponse = [
'foo' => [
'value' => 'bar',
'expireDate' => null,
'sameSite' => 'Lax',
],
'bar' => [
'value' => 'foo',
'expireDate' => new \DateTime('1970-01-01'),
'sameSite' => 'Lax',
]
];
$this->assertEquals($expectedResponse, $this->childResponse->getCookies());
}
public function testSetCookies() {
$expected = [
'foo' => [
'value' => 'bar',
'expireDate' => null,
],
'bar' => [
'value' => 'foo',
'expireDate' => new \DateTime('1970-01-01')
]
];
$this->childResponse->setCookies($expected);
$cookies = $this->childResponse->getCookies();
$this->assertEquals($expected, $cookies);
}
public function testInvalidateCookie() {
$this->childResponse->addCookie('foo', 'bar');
$this->childResponse->invalidateCookie('foo');
$expected = [
'foo' => [
'value' => 'expired',
'expireDate' => new \DateTime('1971-01-01'),
'sameSite' => 'Lax',
]
];
$cookies = $this->childResponse->getCookies();
$this->assertEquals($expected, $cookies);
}
public function testInvalidateCookies() {
$this->childResponse->addCookie('foo', 'bar');
$this->childResponse->addCookie('bar', 'foo');
$expected = [
'foo' => [
'value' => 'bar',
'expireDate' => null,
'sameSite' => 'Lax',
],
'bar' => [
'value' => 'foo',
'expireDate' => null,
'sameSite' => 'Lax',
]
];
$cookies = $this->childResponse->getCookies();
$this->assertEquals($expected, $cookies);
$this->childResponse->invalidateCookies(['foo', 'bar']);
$expected = [
'foo' => [
'value' => 'expired',
'expireDate' => new \DateTime('1971-01-01'),
'sameSite' => 'Lax',
],
'bar' => [
'value' => 'expired',
'expireDate' => new \DateTime('1971-01-01'),
'sameSite' => 'Lax',
]
];
$cookies = $this->childResponse->getCookies();
$this->assertEquals($expected, $cookies);
}
public function testRenderReturnNullByDefault() {
$this->assertEquals(null, $this->childResponse->render());
}
public function testGetStatus() {
$default = $this->childResponse->getStatus();
$this->childResponse->setStatus(Http::STATUS_NOT_FOUND);
$this->assertEquals(Http::STATUS_OK, $default);
$this->assertEquals(Http::STATUS_NOT_FOUND, $this->childResponse->getStatus());
}
public function testGetEtag() {
$this->childResponse->setEtag('hi');
$this->assertSame('hi', $this->childResponse->getEtag());
}
public function testGetLastModified() {
$lastModified = new \DateTime('now', new \DateTimeZone('GMT'));
$lastModified->setTimestamp(1);
$this->childResponse->setLastModified($lastModified);
$this->assertEquals($lastModified, $this->childResponse->getLastModified());
}
public function testCacheSecondsZero() {
$this->childResponse->cacheFor(0);
$headers = $this->childResponse->getHeaders();
$this->assertEquals('no-cache, no-store, must-revalidate', $headers['Cache-Control']);
$this->assertFalse(isset($headers['Pragma']));
$this->assertFalse(isset($headers['Expires']));
}
public function testCacheSeconds() {
$time = $this->createMock(ITimeFactory::class);
$time->method('getTime')
->willReturn(1234567);
$this->overwriteService(ITimeFactory::class, $time);
$this->childResponse->cacheFor(33);
$headers = $this->childResponse->getHeaders();
$this->assertEquals('private, max-age=33, must-revalidate', $headers['Cache-Control']);
$this->assertEquals('private', $headers['Pragma']);
$this->assertEquals('Thu, 15 Jan 1970 06:56:40 +0000', $headers['Expires']);
}
public function testEtagLastModifiedHeaders() {
$lastModified = new \DateTime('now', new \DateTimeZone('GMT'));
$lastModified->setTimestamp(1);
$this->childResponse->setLastModified($lastModified);
$headers = $this->childResponse->getHeaders();
$this->assertEquals('Thu, 01 Jan 1970 00:00:01 +0000', $headers['Last-Modified']);
}
public function testChainability() {
$lastModified = new \DateTime('now', new \DateTimeZone('GMT'));
$lastModified->setTimestamp(1);
$this->childResponse->setEtag('hi')
->setStatus(Http::STATUS_NOT_FOUND)
->setLastModified($lastModified)
->cacheFor(33)
->addHeader('hello', 'world');
$headers = $this->childResponse->getHeaders();
$this->assertEquals('world', $headers['hello']);
$this->assertEquals(Http::STATUS_NOT_FOUND, $this->childResponse->getStatus());
$this->assertEquals('hi', $this->childResponse->getEtag());
$this->assertEquals('Thu, 01 Jan 1970 00:00:01 +0000', $headers['Last-Modified']);
$this->assertEquals('private, max-age=33, must-revalidate',
$headers['Cache-Control']);
}
public function testThrottle() {
$this->assertFalse($this->childResponse->isThrottled());
$this->childResponse->throttle();
$this->assertTrue($this->childResponse->isThrottled());
}
public function testGetThrottleMetadata() {
$this->childResponse->throttle(['foo' => 'bar']);
$this->assertSame(['foo' => 'bar'], $this->childResponse->getThrottleMetadata());
}
}
#n3'>3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/*-
* Copyright 2016 Vsevolod Stakhov
*
* 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.
*/
#ifndef SRC_LIBUTIL_EXPRESSION_H_
#define SRC_LIBUTIL_EXPRESSION_H_
#include "config.h"
#include "mem_pool.h"
#include "fstring.h"
#include "util.h"
#ifdef __cplusplus
extern "C" {
#endif
#define RSPAMD_EXPRESSION_MAX_PRIORITY 1024
#define RSPAMD_EXPRESSION_FLAG_NOOPT (1 << 0)
enum rspamd_expression_op {
OP_INVALID = 0,
OP_PLUS, /* + */
OP_MULT, /* * */
OP_MINUS, /* - */
OP_DIVIDE, /* / */
OP_OR, /* || or | */
OP_AND, /* && or & */
OP_NOT, /* ! */
OP_LT, /* < */
OP_GT, /* > */
OP_LE, /* <= */
OP_GE, /* >= */
OP_EQ, /* == */
OP_NE, /* != */
OP_OBRACE, /* ( */
OP_CBRACE /* ) */
};
typedef struct rspamd_expression_atom_s {
/* Parent node */
GNode *parent;
/* Opaque userdata */
gpointer data;
/* String representation of atom */
const char *str;
/* Length of the string representation of atom */
unsigned int len;
/* Relative priority */
int priority;
unsigned int hits;
struct rspamd_counter_data exec_time;
} rspamd_expression_atom_t;
typedef double (*rspamd_expression_process_cb)(gpointer runtime_data,
rspamd_expression_atom_t *atom);
struct rspamd_atom_subr {
/* Parses atom from string and returns atom structure */
rspamd_expression_atom_t *(*parse)(const char *line, gsize len,
rspamd_mempool_t *pool, gpointer ud, GError **err);
/* Process atom via the opaque pointer (e.g. struct rspamd_task *) */
rspamd_expression_process_cb process;
/* Calculates the relative priority of the expression */
int (*priority)(rspamd_expression_atom_t *atom);
void (*destroy)(rspamd_expression_atom_t *atom);
};
/* Opaque structure */
struct rspamd_expression;
/**
* Parse symbolic expression and create the expression using the specified subroutines for atoms processing
* @param line line to parse
* @param len length of the line (if 0 then line should be NULL terminated)
* @param subr subroutines for atoms parsing
* @param subr_data opaque dat pointer
* @param pool pool to use for memory allocations
* @param err error pointer
* @param target the target expression
* @return TRUE if an expression have been parsed
*/
gboolean rspamd_parse_expression(const char *line, gsize len,
const struct rspamd_atom_subr *subr, gpointer subr_data,
rspamd_mempool_t *pool, GError **err,
struct rspamd_expression **target);
/**
* Process the expression and return its value using atom 'process' functions with the specified data pointer
* @param expr expression to process
* @param data opaque data pointer for all the atoms
* @return the value of expression
*/
double rspamd_process_expression(struct rspamd_expression *expr,
int flags,
gpointer runtime_ud);
/**
* Process the expression and return its value using atom 'process' functions with the specified data pointer.
* This function also accepts `track` argument where it writes matched atoms (those whose value is more than 0)
* @param expr expression to process
* @param data opaque data pointer for all the atoms
* @param track pointer array to atoms tracking
* @return the value of expression
*/
double rspamd_process_expression_track(struct rspamd_expression *expr,
int flags,
gpointer runtime_ud,
GPtrArray **track);
/**
* Process the expression with the custom processor
* @param expr
* @param cb
* @param process_data
* @return
*/
double rspamd_process_expression_closure(struct rspamd_expression *expr,
rspamd_expression_process_cb cb,
int flags,
gpointer runtime_ud,
GPtrArray **track);
/**
* Shows string representation of an expression
* @param expr expression to show
* @return freshly allocated string with expression
*/
GString *rspamd_expression_tostring(struct rspamd_expression *expr);
/**
* Callback that is called on @see rspamd_expression_atom_foreach, atom is ephemeral
* and should not be modified within callback
*/
typedef void (*rspamd_expression_atom_foreach_cb)(const rspamd_ftok_t *atom,
gpointer ud);
/**
* Traverse over all atoms in the expression
* @param expr expression
* @param cb callback to be called
* @param ud opaque data passed to `cb`
*/
void rspamd_expression_atom_foreach(struct rspamd_expression *expr,
rspamd_expression_atom_foreach_cb cb, gpointer cbdata);
/**
* Checks if a specified node in AST is the specified operation
* @param node AST node packed in GNode container
* @param op operation to check
* @return TRUE if node is operation node and is exactly the specified option
*/
gboolean rspamd_expression_node_is_op(GNode *node, enum rspamd_expression_op op);
#ifdef __cplusplus
}
#endif
#endif /* SRC_LIBUTIL_EXPRESSION_H_ */
|