Skip to content

Commit

Permalink
feat: respect X-Forwarded-Proto (DEV-3499) (#453)
Browse files Browse the repository at this point in the history
  • Loading branch information
siers authored Sep 4, 2024
1 parent 9463410 commit d5eaab3
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 85 deletions.
11 changes: 10 additions & 1 deletion shttps/Connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,15 @@ Connection::~Connection()
}
//=============================================================================

/*!
* Return true if a secure (SSL) connection is used
*/
bool Connection::secure(void)
{
bool forwarded = !header("x-forwarded-proto").compare("https");
return _secure || forwarded;
}

int Connection::setupKeepAlive(int default_timeout)
{
if (_keep_alive) {
Expand Down Expand Up @@ -1607,4 +1616,4 @@ bool Connection::cleanupUploads(void)
}
//=============================================================================

}
}// namespace shttps
2 changes: 1 addition & 1 deletion shttps/Connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ class Connection
/*!
* Return true if a secure (SSL) connection is used
*/
inline bool secure(void) { return _secure; }
bool secure(void);

/*!
* Set the secure connection status
Expand Down
27 changes: 7 additions & 20 deletions src/SipiHttpServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -657,20 +657,13 @@ static void serve_info_json_file(Connection &conn_obj,
json_object_set_new(root, "@context", json_string("http://sipi.io/api/file/3/context.json"));
}

std::string proto = conn_obj.secure() ? std::string("https://") : std::string("http://");
std::string host = conn_obj.header("host");
std::string id;
if (params[iiif_prefix] == "") {
if (conn_obj.secure()) {
id = std::string("https://") + host + "/" + params[iiif_identifier];
} else {
id = std::string("http://") + host + "/" + params[iiif_identifier];
}
id = proto + host + "/" + params[iiif_identifier];
} else {
if (conn_obj.secure()) {
id = std::string("https://") + host + "/" + params[iiif_prefix] + "/" + params[iiif_identifier];
} else {
id = std::string("http://") + host + "/" + params[iiif_prefix] + "/" + params[iiif_identifier];
}
id = proto + host + "/" + params[iiif_prefix] + "/" + params[iiif_identifier];
}
json_object_set_new(root, "id", json_string(id.c_str()));

Expand Down Expand Up @@ -927,20 +920,14 @@ static void serve_knora_json_file(Connection &conn_obj,
json_t *root = json_object();
json_object_set_new(root, "@context", json_string("http://sipi.io/api/file/3/context.json"));

std::string proto = conn_obj.secure() ? std::string("https://") : std::string("http://");
std::string host = conn_obj.header("host");
std::string id;

if (params[iiif_prefix] == "") {
if (conn_obj.secure()) {
id = std::string("https://") + host + "/" + params[iiif_identifier];
} else {
id = std::string("http://") + host + "/" + params[iiif_identifier];
}
id = proto + host + "/" + params[iiif_identifier];
} else {
if (conn_obj.secure()) {
id = std::string("https://") + host + "/" + params[iiif_prefix] + "/" + params[iiif_identifier];
} else {
id = std::string("http://") + host + "/" + params[iiif_prefix] + "/" + params[iiif_identifier];
}
id = proto + host + "/" + params[iiif_prefix] + "/" + params[iiif_identifier];
}
json_object_set_new(root, "id", json_string(id.c_str()));

Expand Down
6 changes: 4 additions & 2 deletions test/e2e/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ def post_file(self, url_path, file_path, mime_type, params=None, headers=None):

return response.json()

def get_json(self, url_path, use_ssl=False):
def get_json(self, url_path, use_ssl=False, use_forwarded_ssl=None):
"""
Sends a request which expects JSON
:param url_path: a path that will be appended to the Sipi base URL to make the request.
Expand All @@ -423,8 +423,10 @@ def get_json(self, url_path, use_ssl=False):
else:
sipi_url = self.make_sipi_url(url_path)

x_forwarded_proto = {True: 'https', False: 'http'}.get(use_forwarded_ssl)

try:
response = requests.get(sipi_url)
response = requests.get(sipi_url, headers={'X-Forwarded-Proto': x_forwarded_proto})
response.raise_for_status()
except:
raise SipiTestError("post request to {} failed: {}".format(sipi_url, response.json()["message"]))
Expand Down
124 changes: 63 additions & 61 deletions test/e2e/test_02_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,78 +336,80 @@ def test_knora_info_validation(self, manager):
def test_json_info_validation(self, manager):
"""pass the info.json request tests"""

expected_result = {
'@context': 'http://iiif.io/api/image/3/context.json',
'id': 'http://127.0.0.1:1024/unit/_lena512.jp2',
'type': 'ImageService3',
'protocol': 'http://iiif.io/api/image',
'profile': 'level2',
'width': 512,
'height': 512,
'sizes': [
{'width': 256, 'height': 256},
{'width': 128, 'height': 128}
],
'tiles': [{'width': 512, 'height': 512, 'scaleFactors': [1, 2, 3, 4]}],
'extraFormats': ['tif', 'jp2'],
'preferredFormats': ['jpg', 'tif', 'jp2', 'png'],
'extraFeatures': [
'baseUriRedirect',
'canonicalLinkHeader',
'cors',
'jsonldMediaType',
'mirroring',
'profileLinkHeader',
'regionByPct',
'regionByPx',
'regionSquare',
'rotationArbitrary',
'rotationBy90s',
'sizeByConfinedWh',
'sizeByH',
'sizeByPct',
'sizeByW',
'sizeByWh',
'sizeUpscaling'
]
}
def expected_result(filename, proto='http'):
return {
'@context': 'http://iiif.io/api/image/3/context.json',
'id': proto + '://127.0.0.1:1024/unit/' + filename,
'type': 'ImageService3',
'protocol': 'http://iiif.io/api/image',
'profile': 'level2',
'width': 512,
'height': 512,
'sizes': [
{'width': 256, 'height': 256},
{'width': 128, 'height': 128}
],
'tiles': [{'width': 512, 'height': 512, 'scaleFactors': [1, 2, 3, 4]}],
'extraFormats': ['tif', 'jp2'],
'preferredFormats': ['jpg', 'tif', 'jp2', 'png'],
'extraFeatures': [
'baseUriRedirect',
'canonicalLinkHeader',
'cors',
'jsonldMediaType',
'mirroring',
'profileLinkHeader',
'regionByPct',
'regionByPx',
'regionSquare',
'rotationArbitrary',
'rotationBy90s',
'sizeByConfinedWh',
'sizeByH',
'sizeByPct',
'sizeByW',
'sizeByWh',
'sizeUpscaling'
]
}

response_json = manager.post_file(
"/api/upload", manager.data_dir_path("unit/lena512.tif"), "image/tiff")
filename = response_json["filename"]
response_json = manager.post_file("/api/upload", manager.data_dir_path("unit/lena512.tif"), "image/tiff")

manager.expect_status_code(
"/unit/{}/full/max/0/default.jpg".format(filename), 200)
filename = response_json["filename"]
manager.expect_status_code("/unit/{}/full/max/0/default.jpg".format(filename), 200)

response_json = manager.get_json("/unit/{}/info.json".format(filename))
expected_result["id"] = "http://127.0.0.1:1024/unit/{}".format(
filename)
assert response_json == expected_result
assert response_json == expected_result(filename)

# response_json = manager.get_json("/unit/{}/info.json".format(filename), use_ssl=True)
# expected_result["id"] = "https://127.0.0.1:1024/unit/{}".format(filename)
# assert response_json == expected_result
# assert response_json == expected_result(filename)

response_json = manager.get_json("/unit/{}/info.json".format(filename), use_forwarded_ssl=False)
assert response_json == expected_result(filename, proto='http')

response_json = manager.get_json("/unit/{}/info.json".format(filename), use_forwarded_ssl=True)
assert response_json == expected_result(filename, proto='https')

def test_knora_json_for_video(self, manager):
"""pass the knora.json request for video"""

expected_result = {
"@context": "http://sipi.io/api/file/3/context.json",
"id": "http://127.0.0.1:1024/unit/8pdET49BfoJ-EeRcIbgcLch.mp4",
"checksumOriginal": "19cc4bccad39c89cc44936ef69565bb933d41a065fd59d666d58e5ef344e8149",
"checksumDerivative": "19cc4bccad39c89cc44936ef69565bb933d41a065fd59d666d58e5ef344e8149",
"internalMimeType": "video/mp4",
"fileSize": 475205,
"originalFilename": "Dummy.mp4",
"duration": 4.7000000000000002,
"fps": 30,
"height": 240,
"width": 320
}
def expected_result(proto):
return {
"@context": "http://sipi.io/api/file/3/context.json",
"id": proto + "://127.0.0.1:1024/unit/8pdET49BfoJ-EeRcIbgcLch.mp4",
"checksumOriginal": "19cc4bccad39c89cc44936ef69565bb933d41a065fd59d666d58e5ef344e8149",
"checksumDerivative": "19cc4bccad39c89cc44936ef69565bb933d41a065fd59d666d58e5ef344e8149",
"internalMimeType": "video/mp4",
"fileSize": 475205,
"originalFilename": "Dummy.mp4",
"duration": 4.7000000000000002,
"fps": 30,
"height": 240,
"width": 320
}

response_json = manager.get_json(
"/unit/8pdET49BfoJ-EeRcIbgcLch.mp4/knora.json")
assert response_json == expected_result
assert manager.get_json("/unit/8pdET49BfoJ-EeRcIbgcLch.mp4/knora.json") == expected_result('http')
assert manager.get_json("/unit/8pdET49BfoJ-EeRcIbgcLch.mp4/knora.json", use_forwarded_ssl=True) == expected_result('https')

def test_handling_of_missing_sidecar_file_for_video(self, manager):
"""correctly handle missing sidecar file for video"""
Expand Down

0 comments on commit d5eaab3

Please sign in to comment.