Skip to content

Commit ddef0d5

Browse files
apolukhinsegoon
authored andcommitted
feat core: directory files and 404 files for static handler
Tests: протестировано CI Pull Request resolved: #962 commit_hash:95dac1048d9078449c658faaa8cc617f3d213e0c
1 parent 4743b5d commit ddef0d5

File tree

7 files changed

+90
-14
lines changed

7 files changed

+90
-14
lines changed

.mapping.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3699,8 +3699,10 @@
36993699
"samples/sqlite_service/tests/test_sqlite.py":"taxi/uservices/userver/samples/sqlite_service/tests/test_sqlite.py",
37003700
"samples/static_service/CMakeLists.txt":"taxi/uservices/userver/samples/static_service/CMakeLists.txt",
37013701
"samples/static_service/main.cpp":"taxi/uservices/userver/samples/static_service/main.cpp",
3702+
"samples/static_service/public/404.html":"taxi/uservices/userver/samples/static_service/public/404.html",
37023703
"samples/static_service/public/dir1/.hidden_file.txt":"taxi/uservices/userver/samples/static_service/public/dir1/.hidden_file.txt",
37033704
"samples/static_service/public/dir1/dir2/data.html":"taxi/uservices/userver/samples/static_service/public/dir1/dir2/data.html",
3705+
"samples/static_service/public/dir1/dir2/index.html":"taxi/uservices/userver/samples/static_service/public/dir1/dir2/index.html",
37043706
"samples/static_service/public/index.html":"taxi/uservices/userver/samples/static_service/public/index.html",
37053707
"samples/static_service/static_config.yaml":"taxi/uservices/userver/samples/static_service/static_config.yaml",
37063708
"samples/static_service/tests-serve-from-root/conftest.py":"taxi/uservices/userver/samples/static_service/tests-serve-from-root/conftest.py",

core/include/userver/server/handlers/http_handler_static.hpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ namespace server::handlers {
3636
/// Inherits all the options from server::handlers::HttpHandlerBase and adds the
3737
/// following ones:
3838
///
39-
/// Name | Description | Default value
40-
/// ------------------ | ----------------------------------------- | -------------
41-
/// fs-cache-component | Name of the components::FsCache component | fs-cache-component
42-
/// expires | Cache age in seconds | 600
39+
/// Name | Description | Default value
40+
/// ------------------ | ----------------------------------------------------------------------------------------- | -------------
41+
/// fs-cache-component | Name of the components::FsCache component | fs-cache-component
42+
/// expires | Cache age in seconds | 600
43+
/// directory-file | File to return for directory requests. File name (not path) search in requested directory | "index.html"
44+
/// not-found-file | File to return for missing files | "/404.html"
4345
///
4446
/// ## Example usage:
4547
///
@@ -65,6 +67,8 @@ class HttpHandlerStatic final : public HttpHandlerBase {
6567
dynamic_config::Source config_;
6668
const fs::FsCacheClient& storage_;
6769
const std::chrono::seconds cache_age_;
70+
const std::string directory_file_;
71+
const std::string not_found_file_;
6872
};
6973

7074
} // namespace server::handlers

core/src/server/handlers/http_handler_static.cpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ HttpHandlerStatic::HttpHandlerStatic(
2323
context.FindComponent<components::FsCache>(config["fs-cache-component"].As<std::string>("fs-cache-component"))
2424
.GetClient()
2525
),
26-
cache_age_(config["expires"].As<std::chrono::seconds>(600)) {}
26+
cache_age_(config["expires"].As<std::chrono::seconds>(600)),
27+
directory_file_(config["directory-file"].As<std::string>("index.html")),
28+
not_found_file_(config["not-found-file"].As<std::string>("/404.html")) {}
2729

2830
std::string HttpHandlerStatic::HandleRequestThrow(const http::HttpRequest& request, request::RequestContext&) const {
2931
std::string search_path;
@@ -38,7 +40,23 @@ std::string HttpHandlerStatic::HandleRequestThrow(const http::HttpRequest& reque
3840
LOG_DEBUG() << "search_path: " << search_path;
3941

4042
auto& response = request.GetHttpResponse();
41-
const auto file = storage_.TryGetFile(search_path);
43+
auto file = storage_.TryGetFile(search_path);
44+
if (!file && !directory_file_.empty()) {
45+
if (directory_file_.front() == '/') {
46+
search_path = directory_file_;
47+
} else if (search_path.empty() || search_path[search_path.size() - 1] != '/') {
48+
search_path += "/" + directory_file_;
49+
} else {
50+
search_path += directory_file_;
51+
}
52+
LOG_DEBUG() << "search_path 2: " << search_path;
53+
file = storage_.TryGetFile(search_path);
54+
}
55+
if (!file) {
56+
file = storage_.TryGetFile(not_found_file_);
57+
response.SetStatusNotFound();
58+
}
59+
4260
if (file) {
4361
const auto config = config_.GetSnapshot();
4462
response.SetHeader(USERVER_NAMESPACE::http::headers::kExpires, std::to_string(cache_age_.count()));
@@ -65,6 +83,14 @@ additionalProperties: false
6583
type: string
6684
description: Cache age in seconds
6785
defaultDescription: 600
86+
directory-file:
87+
type: string
88+
description: File to return for directory requests. File name (not path) search in requested directory
89+
defaultDescription: index.html
90+
not-found-file:
91+
type: string
92+
description: File to return for missing files
93+
defaultDescription: /404.html
6894
)");
6995
}
7096

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<html>
2+
<head>
3+
<title>userver</title>
4+
</head>
5+
<body>
6+
File not found
7+
</body>
8+
</html>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
index.html file in subdirs

samples/static_service/tests-serve-from-root/test_static.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
import pytest
2+
3+
14
async def test_file_not_found(service_client):
25
response = await service_client.get('/file.not')
36
assert response.status == 404
4-
assert response.content == b'File not found'
7+
assert b'File not found' in response.content
58

69

7-
async def test_file(service_client, service_source_dir):
8-
response = await service_client.get('/index.html')
10+
@pytest.mark.parametrize('path', ['/index.html', '/'])
11+
async def test_file(service_client, service_source_dir, path):
12+
response = await service_client.get(path)
913
assert response.status == 200
1014
assert response.headers['Content-Type'] == 'text/html'
1115
assert response.headers['Expires'] == '600'
@@ -22,7 +26,17 @@ async def test_file_recursive(service_client, service_source_dir):
2226
assert response.content.decode() == file.open().read()
2327

2428

29+
@pytest.mark.parametrize('path', ['/dir1/dir2', '/dir1/dir2/'])
30+
async def test_file_recursive_index(service_client, service_source_dir, path):
31+
response = await service_client.get(path)
32+
assert response.status == 200
33+
assert response.headers['Content-Type'] == 'text/html'
34+
file = service_source_dir.joinpath('public') / 'dir1' / 'dir2' / 'index.html'
35+
assert response.content.decode() == file.open().read()
36+
37+
2538
async def test_hidden_file(service_client, service_source_dir):
2639
response = await service_client.get('/dir1/.hidden_file.txt')
2740
assert response.status == 404
28-
assert response.content.decode() == 'File not found'
41+
file = service_source_dir.joinpath('public') / '404.html'
42+
assert response.content.decode() == file.open().read()

samples/static_service/tests-serve-from-subpath/test_static.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
1+
import pytest
2+
3+
14
async def test_file_not_found(service_client):
25
response = await service_client.get('/possible/to/work/from/subpath/file.not')
36
assert response.status == 404
4-
assert response.content == b'File not found'
7+
assert b'File not found' in response.content
58

69

7-
async def test_file(service_client, service_source_dir):
8-
response = await service_client.get('/possible/to/work/from/subpath/index.html')
10+
@pytest.mark.parametrize(
11+
'path',
12+
[
13+
'/possible/to/work/from/subpath/index.html',
14+
],
15+
)
16+
async def test_file(service_client, service_source_dir, path):
17+
response = await service_client.get(path)
918
assert response.status == 200
1019
assert response.headers['Content-Type'] == 'text/html'
1120
assert response.headers['Expires'] == '600'
@@ -22,7 +31,19 @@ async def test_file_recursive(service_client, service_source_dir):
2231
assert response.content.decode() == file.open().read()
2332

2433

34+
@pytest.mark.parametrize(
35+
'path', ['/possible/to/work/from/subpath/dir1/dir2', '/possible/to/work/from/subpath/dir1/dir2/']
36+
)
37+
async def test_file_recursive_index(service_client, service_source_dir, path):
38+
response = await service_client.get(path)
39+
assert response.status == 200
40+
assert response.headers['Content-Type'] == 'text/html'
41+
file = service_source_dir.joinpath('public') / 'dir1' / 'dir2' / 'index.html'
42+
assert response.content.decode() == file.open().read()
43+
44+
2545
async def test_hidden_file(service_client, service_source_dir):
2646
response = await service_client.get('/possible/to/work/from/subpath/dir1/.hidden_file.txt')
2747
assert response.status == 404
28-
assert response.content.decode() == 'File not found'
48+
file = service_source_dir.joinpath('public') / '404.html'
49+
assert response.content.decode() == file.open().read()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy