Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
surykatka
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
2
Merge Requests
2
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
surykatka
Commits
506522e1
Commit
506522e1
authored
Jul 23, 2020
by
Romain Courteaud
🐙
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Store the http response headers
parent
65fd3852
Changes
6
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
373 additions
and
11 deletions
+373
-11
src/surykatka/bot.py
src/surykatka/bot.py
+1
-0
src/surykatka/db.py
src/surykatka/db.py
+20
-4
src/surykatka/http.py
src/surykatka/http.py
+53
-1
tests/test_bot.py
tests/test_bot.py
+7
-0
tests/test_db.py
tests/test_db.py
+46
-5
tests/test_http.py
tests/test_http.py
+246
-1
No files found.
src/surykatka/bot.py
View file @
506522e1
...
@@ -298,6 +298,7 @@ class WebBot:
...
@@ -298,6 +298,7 @@ class WebBot:
result_dict
[
"http_query"
].
append
(
result_dict
[
"http_query"
].
append
(
{
{
"status_code"
:
network_change
[
"status_code"
],
"status_code"
:
network_change
[
"status_code"
],
"http_header_dict"
:
network_change
[
"http_header_dict"
],
"total_seconds"
:
network_change
[
"total_seconds"
],
"total_seconds"
:
network_change
[
"total_seconds"
],
"url"
:
network_change
[
"url"
],
"url"
:
network_change
[
"url"
],
"ip"
:
network_change
[
"ip"
],
"ip"
:
network_change
[
"ip"
],
...
...
src/surykatka/db.py
View file @
506522e1
...
@@ -18,7 +18,7 @@
...
@@ -18,7 +18,7 @@
# See https://www.nexedi.com/licensing for rationale and options.
# See https://www.nexedi.com/licensing for rationale and options.
import
peewee
import
peewee
from
playhouse.sqlite_ext
import
SqliteExtDatabase
from
playhouse.sqlite_ext
import
SqliteExtDatabase
,
JSONField
import
datetime
import
datetime
from
playhouse.migrate
import
migrate
,
SqliteMigrator
from
playhouse.migrate
import
migrate
,
SqliteMigrator
...
@@ -115,6 +115,7 @@ class LogDB:
...
@@ -115,6 +115,7 @@ class LogDB:
ip
=
peewee
.
TextField
()
ip
=
peewee
.
TextField
()
url
=
peewee
.
TextField
()
url
=
peewee
.
TextField
()
status_code
=
peewee
.
IntegerField
()
status_code
=
peewee
.
IntegerField
()
http_header_dict
=
JSONField
(
default
=
dict
)
total_seconds
=
peewee
.
FloatField
(
default
=
0
)
total_seconds
=
peewee
.
FloatField
(
default
=
0
)
class
Meta
:
class
Meta
:
...
@@ -131,7 +132,7 @@ class LogDB:
...
@@ -131,7 +132,7 @@ class LogDB:
def
createTables
(
self
):
def
createTables
(
self
):
# http://www.sqlite.org/pragma.html#pragma_user_version
# http://www.sqlite.org/pragma.html#pragma_user_version
db_version
=
self
.
_db
.
pragma
(
"user_version"
)
db_version
=
self
.
_db
.
pragma
(
"user_version"
)
expected_version
=
3
expected_version
=
4
if
db_version
!=
expected_version
:
if
db_version
!=
expected_version
:
with
self
.
_db
.
transaction
():
with
self
.
_db
.
transaction
():
...
@@ -152,10 +153,12 @@ class LogDB:
...
@@ -152,10 +153,12 @@ class LogDB:
# version 1 without SSL support
# version 1 without SSL support
self
.
_db
.
create_tables
([
self
.
SslChange
])
self
.
_db
.
create_tables
([
self
.
SslChange
])
migrator
=
SqliteMigrator
(
self
.
_db
)
migration_list
=
[]
if
(
0
<
db_version
)
and
(
db_version
<=
2
):
if
(
0
<
db_version
)
and
(
db_version
<=
2
):
# version 2 without the http total_seconds column
# version 2 without the http total_seconds column
migrator
=
SqliteMigrator
(
self
.
_db
)
migration_list
.
append
(
migrate
(
migrator
.
add_column
(
migrator
.
add_column
(
"HttpCodeChange"
,
"HttpCodeChange"
,
"total_seconds"
,
"total_seconds"
,
...
@@ -163,6 +166,19 @@ class LogDB:
...
@@ -163,6 +166,19 @@ class LogDB:
)
)
)
)
if
(
0
<
db_version
)
and
(
db_version
<=
3
):
# version 3 without the http header column
migration_list
.
append
(
migrator
.
add_column
(
"HttpCodeChange"
,
"http_header_dict"
,
self
.
HttpCodeChange
.
http_header_dict
,
)
)
if
migration_list
:
migrate
(
*
migration_list
)
if
db_version
>=
expected_version
:
if
db_version
>=
expected_version
:
raise
ValueError
(
"Can not downgrade SQLite DB"
)
raise
ValueError
(
"Can not downgrade SQLite DB"
)
...
...
src/surykatka/http.py
View file @
506522e1
...
@@ -120,7 +120,17 @@ def calculateSpeedRange(total_seconds, fast, moderate):
...
@@ -120,7 +120,17 @@ def calculateSpeedRange(total_seconds, fast, moderate):
return
"SLOW"
return
"SLOW"
def
logHttpStatus
(
db
,
ip
,
url
,
code
,
total_seconds
,
fast
,
moderate
,
status_id
):
def
logHttpStatus
(
db
,
ip
,
url
,
code
,
http_header_dict
,
total_seconds
,
fast
,
moderate
,
status_id
,
):
with
db
.
_db
.
atomic
():
with
db
.
_db
.
atomic
():
try
:
try
:
...
@@ -132,6 +142,7 @@ def logHttpStatus(db, ip, url, code, total_seconds, fast, moderate, status_id):
...
@@ -132,6 +142,7 @@ def logHttpStatus(db, ip, url, code, total_seconds, fast, moderate, status_id):
if
(
if
(
(
previous_entry
is
None
)
(
previous_entry
is
None
)
or
(
previous_entry
.
status_code
!=
code
)
or
(
previous_entry
.
status_code
!=
code
)
or
(
previous_entry
.
http_header_dict
!=
http_header_dict
)
or
(
or
(
calculateSpeedRange
(
calculateSpeedRange
(
previous_entry
.
total_seconds
,
fast
,
moderate
previous_entry
.
total_seconds
,
fast
,
moderate
...
@@ -144,6 +155,7 @@ def logHttpStatus(db, ip, url, code, total_seconds, fast, moderate, status_id):
...
@@ -144,6 +155,7 @@ def logHttpStatus(db, ip, url, code, total_seconds, fast, moderate, status_id):
ip
=
ip
,
ip
=
ip
,
url
=
url
,
url
=
url
,
status_code
=
code
,
status_code
=
code
,
http_header_dict
=
http_header_dict
,
total_seconds
=
total_seconds
,
total_seconds
=
total_seconds
,
)
)
return
previous_entry
.
status_id
return
previous_entry
.
status_id
...
@@ -182,11 +194,51 @@ def checkHttpStatus(
...
@@ -182,11 +194,51 @@ def checkHttpStatus(
response
=
request
(
response
=
request
(
ip_url
,
headers
=
{
"Host"
:
hostname
},
version
=
bot_version
,
**
request_kw
ip_url
,
headers
=
{
"Host"
:
hostname
},
version
=
bot_version
,
**
request_kw
)
)
# Blacklisted, because of non stability
# 'Date', 'Age', 'Expires'
header_list
=
[
# Redirect
"Location"
,
# HTTP Range
"Accept-Ranges"
,
# HTTP Cache
"Etag"
,
"Last-Modified"
,
"Vary"
,
"Cache-Control"
,
"Set-Cookie"
,
"WWW-Authenticate"
# gzip
"Content-Type"
,
"Content-Encoding"
,
"Content-Disposition"
# Security
"Content-Security-Policy"
,
"Referrer-Policy"
,
"Strict-Transport-Policy"
,
"Feature-Policy"
,
"X-Frame-Options"
,
"X-Content-Type-Options"
# CORS
"Access-Control-Allow-Origin"
,
"Access-Control-Allow-Methods"
,
"Access-Control-Allow-Credentials"
,
"Access-Control-Allow-Headers"
,
"Access-Control-Expose-Headers"
,
]
header_dict
=
{}
for
header_key
in
header_list
:
header_value
=
response
.
headers
.
get
(
header_key
,
None
)
if
header_value
is
not
None
:
header_dict
[
header_key
]
=
header_value
logHttpStatus
(
logHttpStatus
(
db
,
db
,
ip
,
ip
,
url
,
url
,
response
.
status_code
,
response
.
status_code
,
header_dict
,
response
.
elapsed
.
total_seconds
(),
response
.
elapsed
.
total_seconds
(),
elapsed_fast
,
elapsed_fast
,
elapsed_moderate
,
elapsed_moderate
,
...
...
tests/test_bot.py
View file @
506522e1
...
@@ -115,6 +115,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
...
@@ -115,6 +115,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
"surykatka.http.request"
"surykatka.http.request"
)
as
mock_request
:
)
as
mock_request
:
mock_request
.
return_value
.
headers
=
{
"Etag"
:
"foobar"
}
mock_get_default_resolver
.
return_value
=
resolver
mock_get_default_resolver
.
return_value
=
resolver
mock_query
.
return_value
=
[
MockAnswer
(
"1.2.3.4"
)]
mock_query
.
return_value
=
[
MockAnswer
(
"1.2.3.4"
)]
mock_create_default_context
.
return_value
.
wrap_socket
.
return_value
.
getpeercert
.
side_effect
=
[
mock_create_default_context
.
return_value
.
wrap_socket
.
return_value
.
getpeercert
.
side_effect
=
[
...
@@ -178,6 +179,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
...
@@ -178,6 +179,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
"surykatka.http.request"
"surykatka.http.request"
)
as
mock_request
:
)
as
mock_request
:
mock_request
.
return_value
.
headers
=
{
"Etag"
:
"foobar"
}
mock_query
.
return_value
=
[
MockAnswer
(
"1.2.3.4"
)]
mock_query
.
return_value
=
[
MockAnswer
(
"1.2.3.4"
)]
mock_create_default_context
.
return_value
.
wrap_socket
.
return_value
.
getpeercert
.
side_effect
=
[
mock_create_default_context
.
return_value
.
wrap_socket
.
return_value
.
getpeercert
.
side_effect
=
[
b""
,
b""
,
...
@@ -244,6 +246,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
...
@@ -244,6 +246,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
"surykatka.http.request"
"surykatka.http.request"
)
as
mock_request
:
)
as
mock_request
:
mock_request
.
return_value
.
headers
=
{
"Etag"
:
"foobar"
}
mock_query
.
return_value
=
[
MockAnswer
(
"1.2.3.4"
)]
mock_query
.
return_value
=
[
MockAnswer
(
"1.2.3.4"
)]
mock_create_default_context
.
return_value
.
wrap_socket
.
return_value
.
getpeercert
.
side_effect
=
[
mock_create_default_context
.
return_value
.
wrap_socket
.
return_value
.
getpeercert
.
side_effect
=
[
b""
,
b""
,
...
@@ -312,6 +315,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
...
@@ -312,6 +315,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
"surykatka.http.request"
"surykatka.http.request"
)
as
mock_request
:
)
as
mock_request
:
mock_request
.
return_value
.
headers
=
{
"Etag"
:
"foobar"
}
mock_query
.
return_value
=
[
mock_query
.
return_value
=
[
MockAnswer
(
"1.2.3.4"
),
MockAnswer
(
"1.2.3.4"
),
MockAnswer
(
"1.2.3.5"
),
MockAnswer
(
"1.2.3.5"
),
...
@@ -392,6 +396,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
...
@@ -392,6 +396,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
"surykatka.http.request"
"surykatka.http.request"
)
as
mock_request
:
)
as
mock_request
:
mock_request
.
return_value
.
headers
=
{
"Etag"
:
"foobar"
}
mock_query
.
return_value
=
[
MockAnswer
(
"1.2.3.4"
)]
mock_query
.
return_value
=
[
MockAnswer
(
"1.2.3.4"
)]
mock_create_default_context
.
return_value
.
wrap_socket
.
return_value
.
getpeercert
.
side_effect
=
[
mock_create_default_context
.
return_value
.
wrap_socket
.
return_value
.
getpeercert
.
side_effect
=
[
b""
,
b""
,
...
@@ -460,6 +465,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
...
@@ -460,6 +465,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
"surykatka.http.request"
"surykatka.http.request"
)
as
mock_request
:
)
as
mock_request
:
mock_request
.
return_value
.
headers
=
{
"Etag"
:
"foobar"
}
mock_query
.
return_value
=
[
MockAnswer
(
"1.2.3.4"
)]
mock_query
.
return_value
=
[
MockAnswer
(
"1.2.3.4"
)]
mock_create_default_context
.
return_value
.
wrap_socket
.
return_value
.
getpeercert
.
side_effect
=
[
mock_create_default_context
.
return_value
.
wrap_socket
.
return_value
.
getpeercert
.
side_effect
=
[
b""
,
b""
,
...
@@ -515,6 +521,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
...
@@ -515,6 +521,7 @@ class SurykatkaBotTestCase(unittest.TestCase):
"surykatka.http.request"
"surykatka.http.request"
)
as
mock_request
:
)
as
mock_request
:
mock_request
.
return_value
.
headers
=
{
"Etag"
:
"foobar"
}
mock_query
.
side_effect
=
[[
MockAnswer
(
"1.2.3.4"
)],
[]]
mock_query
.
side_effect
=
[[
MockAnswer
(
"1.2.3.4"
)],
[]]
bot
.
iterateLoop
()
bot
.
iterateLoop
()
...
...
tests/test_db.py
View file @
506522e1
...
@@ -48,8 +48,10 @@ def validate_schema(model):
...
@@ -48,8 +48,10 @@ def validate_schema(model):
for
column
in
intersect
:
for
column
in
intersect
:
field
=
model
.
_meta
.
columns
[
column
]
field
=
model
.
_meta
.
columns
[
column
]
db_field
=
db_model
.
_meta
.
columns
[
column
]
db_field
=
db_model
.
_meta
.
columns
[
column
]
if
(
field
.
field_type
!=
db_field
.
field_type
)
and
(
if
(
not
(
field
.
field_type
==
"BIGINT"
)
(
field
.
field_type
!=
db_field
.
field_type
)
and
(
not
(
field
.
field_type
==
"BIGINT"
))
and
(
not
field
.
field_type
==
"JSON"
)
):
):
to_change
.
append
((
field
,
db_field
))
to_change
.
append
((
field
,
db_field
))
...
@@ -64,7 +66,7 @@ class SurykatkaDBTestCase(unittest.TestCase):
...
@@ -64,7 +66,7 @@ class SurykatkaDBTestCase(unittest.TestCase):
def
test_createTable
(
self
):
def
test_createTable
(
self
):
assert
self
.
db
.
_db
.
pragma
(
"user_version"
)
==
0
assert
self
.
db
.
_db
.
pragma
(
"user_version"
)
==
0
self
.
db
.
createTables
()
self
.
db
.
createTables
()
assert
self
.
db
.
_db
.
pragma
(
"user_version"
)
==
3
assert
self
.
db
.
_db
.
pragma
(
"user_version"
)
==
4
def
test_downgrade
(
self
):
def
test_downgrade
(
self
):
assert
self
.
db
.
_db
.
pragma
(
"user_version"
)
==
0
assert
self
.
db
.
_db
.
pragma
(
"user_version"
)
==
0
...
@@ -97,10 +99,15 @@ class SurykatkaDBTestCase(unittest.TestCase):
...
@@ -97,10 +99,15 @@ class SurykatkaDBTestCase(unittest.TestCase):
"HttpCodeChange"
,
"total_seconds"
"HttpCodeChange"
,
"total_seconds"
),
),
)
)
migrate
(
SqliteMigrator
(
self
.
db
.
_db
).
drop_column
(
"HttpCodeChange"
,
"http_header_dict"
),
)
self
.
db
.
_db
.
pragma
(
"user_version"
,
1
)
self
.
db
.
_db
.
pragma
(
"user_version"
,
1
)
self
.
db
.
createTables
()
self
.
db
.
createTables
()
assert
self
.
db
.
_db
.
pragma
(
"user_version"
)
==
3
assert
self
.
db
.
_db
.
pragma
(
"user_version"
)
==
4
assert
validate_schema
(
self
.
db
.
SslChange
).
valid
,
validate_schema
(
assert
validate_schema
(
self
.
db
.
SslChange
).
valid
,
validate_schema
(
self
.
db
.
SslChange
self
.
db
.
SslChange
)
)
...
@@ -126,10 +133,44 @@ class SurykatkaDBTestCase(unittest.TestCase):
...
@@ -126,10 +133,44 @@ class SurykatkaDBTestCase(unittest.TestCase):
"HttpCodeChange"
,
"total_seconds"
"HttpCodeChange"
,
"total_seconds"
),
),
)
)
migrate
(
SqliteMigrator
(
self
.
db
.
_db
).
drop_column
(
"HttpCodeChange"
,
"http_header_dict"
),
)
self
.
db
.
_db
.
pragma
(
"user_version"
,
2
)
self
.
db
.
_db
.
pragma
(
"user_version"
,
2
)
self
.
db
.
createTables
()
self
.
db
.
createTables
()
assert
self
.
db
.
_db
.
pragma
(
"user_version"
)
==
3
assert
self
.
db
.
_db
.
pragma
(
"user_version"
)
==
4
assert
validate_schema
(
self
.
db
.
HttpCodeChange
).
valid
,
validate_schema
(
self
.
db
.
HttpCodeChange
)
def
test_migrationFromVersion3
(
self
):
assert
self
.
db
.
_db
.
pragma
(
"user_version"
)
==
0
# Recreate version 3
with
self
.
db
.
_db
.
transaction
():
self
.
db
.
_db
.
create_tables
(
[
self
.
db
.
Status
,
self
.
db
.
ConfigurationChange
,
self
.
db
.
HttpCodeChange
,
self
.
db
.
NetworkChange
,
self
.
db
.
PlatformChange
,
self
.
db
.
DnsChange
,
self
.
db
.
SslChange
,
]
)
migrate
(
SqliteMigrator
(
self
.
db
.
_db
).
drop_column
(
"HttpCodeChange"
,
"http_header_dict"
),
)
self
.
db
.
_db
.
pragma
(
"user_version"
,
3
)
self
.
db
.
createTables
()
assert
self
.
db
.
_db
.
pragma
(
"user_version"
)
==
4
assert
validate_schema
(
self
.
db
.
HttpCodeChange
).
valid
,
validate_schema
(
assert
validate_schema
(
self
.
db
.
HttpCodeChange
).
valid
,
validate_schema
(
self
.
db
.
HttpCodeChange
self
.
db
.
HttpCodeChange
)
)
...
...
tests/test_http.py
View file @
506522e1
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment