Skip to content

Commit b9d5a27

Browse files
npoltorapavlomfiess
authored and
mfiess
committed
HTTP access control (CORS) for pxArchives (pxscene#604)
* HTTP access control (CORS) for pxArchives * build time option for PXCORE_ACCESS_CONTROL_CHECK * disable server functionality for http/https js modules
1 parent 01c7e2b commit b9d5a27

File tree

11 files changed

+187
-11
lines changed

11 files changed

+187
-11
lines changed

examples/pxScene2d/src/pxArchive.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pxArchive::~pxArchive()
1313
gUIThreadQueue.removeAllTasksForObject(this);
1414
}
1515

16-
rtError pxArchive::initFromUrl(const rtString& url)
16+
rtError pxArchive::initFromUrl(const rtString& url, const rtString& origin)
1717
{
1818
mReady = new rtPromise;
1919
mLoadStatus = new rtMapObject;
@@ -30,6 +30,12 @@ rtError pxArchive::initFromUrl(const rtString& url)
3030
mLoadStatus.set("sourceType", "http");
3131
mLoadStatus.set("statusCode", -1);
3232
mDownloadRequest = new rtFileDownloadRequest(url, this);
33+
if (!origin.isEmpty())
34+
{
35+
rtString headerOrigin("Origin:");
36+
headerOrigin.append(origin.cString());
37+
mDownloadRequest->additionalHttpHeaders().push_back(headerOrigin);
38+
}
3339
mDownloadRequest->setCallbackFunction(pxArchive::onDownloadComplete);
3440
rtFileDownloader::instance()->addToDownloadQueue(mDownloadRequest);
3541
}

examples/pxScene2d/src/pxArchive.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class pxArchive: public rtObject
4545
pxArchive();
4646
virtual ~pxArchive();
4747

48-
rtError initFromUrl(const rtString& url);
48+
rtError initFromUrl(const rtString& url, const rtString& origin = rtString());
4949
rtError ready(rtObjectRef& r) const;
5050

5151
rtError loadStatus(rtObjectRef& v) const;

examples/pxScene2d/src/pxScene2d.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "rtString.h"
2929
#include "rtNode.h"
3030
#include "rtPathUtils.h"
31+
#include "rtUrlUtils.h"
3132

3233
#include "pxCore.h"
3334
#include "pxOffscreen.h"
@@ -1538,7 +1539,7 @@ rtDefineObject(pxRoot,pxObject);
15381539
int gTag = 0;
15391540

15401541
pxScene2d::pxScene2d(bool top, pxScriptView* scriptView)
1541-
: start(0), sigma_draw(0), sigma_update(0), end2(0), frameCount(0), mWidth(0), mHeight(0), mStopPropagation(false), mContainer(NULL), mShowDirtyRectangle(false),
1542+
: start(0), sigma_draw(0), sigma_update(0), end2(0), frameCount(0), mWidth(0), mHeight(0), mStopPropagation(false), mContainer(NULL), mShowDirtyRectangle(false),
15421543
mInnerpxObjects(), mDirty(true), mTestView(NULL), mDisposed(false)
15431544
{
15441545
mRoot = new pxRoot(this);
@@ -1548,6 +1549,11 @@ pxScene2d::pxScene2d(bool top, pxScriptView* scriptView)
15481549
mScriptView = scriptView;
15491550
mTag = gTag++;
15501551

1552+
if (scriptView != NULL)
1553+
{
1554+
mOrigin = rtUrlGetOrigin(scriptView->getUrl().cString());
1555+
}
1556+
15511557
// make sure that initial onFocus is sent
15521558
rtObjectRef e = new rtMapObject;
15531559
mRoot->setFocusInternal(true);

examples/pxScene2d/src/pxScene2d.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,7 +1297,7 @@ class pxScene2d: public rtObject, public pxIView
12971297
rtMethodNoArgAndNoReturn("dispose",dispose);
12981298

12991299
pxScene2d(bool top = true, pxScriptView* scriptView = NULL);
1300-
virtual ~pxScene2d()
1300+
virtual ~pxScene2d()
13011301
{
13021302
rtLogDebug("***** deleting pxScene2d\n");
13031303
if (mTestView != NULL)
@@ -1456,7 +1456,7 @@ class pxScene2d: public rtObject, public pxIView
14561456
{
14571457
rtError e = RT_FAIL;
14581458
rtRef<pxArchive> a = new pxArchive;
1459-
if (a->initFromUrl(url) == RT_OK)
1459+
if (a->initFromUrl(url, mOrigin) == RT_OK)
14601460
{
14611461
archive = a;
14621462
e = RT_OK;
@@ -1519,6 +1519,7 @@ class pxScene2d: public rtObject, public pxIView
15191519
#endif
15201520
bool mPointerHidden;
15211521
std::vector<rtObjectRef> mInnerpxObjects;
1522+
rtString mOrigin;
15221523
public:
15231524
void hidePointer( bool hide )
15241525
{

examples/pxScene2d/src/rcvrcore/http_wrap.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ function HttpWrap(accessControl) {
99
HttpWrap.prototype.IncomingMessage = http.IncomingMessage;
1010
HttpWrap.prototype.METHODS = http.METHODS;
1111
HttpWrap.prototype.OutgoingMessage = http.OutgoingMessage;
12-
HttpWrap.prototype.ServerResponse = http.ServerResponse;
13-
HttpWrap.prototype.STATUS_CODES = http.STATUS_CODES;
14-
HttpWrap.prototype.Server = http.Server;
15-
HttpWrap.prototype.createServer = http.createServer;
1612
HttpWrap.prototype.globalAgent = http.globalAgent;
1713

14+
// Server functionality needs to be disabled.
15+
//HttpWrap.prototype.ServerResponse = http.ServerResponse;
16+
//HttpWrap.prototype.STATUS_CODES = http.STATUS_CODES;
17+
//HttpWrap.prototype.Server = http.Server;
18+
//HttpWrap.prototype.createServer = http.createServer;
19+
1820
HttpWrap.prototype.request = function (options, cb) {
1921
if (_accessControl) {
2022
if (_accessControl.isLocalAccessFromRemote(options)) {

examples/pxScene2d/src/rcvrcore/https_wrap.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ function HttpsWrap(accessControl) {
77
var _accessControl = accessControl;
88

99
HttpsWrap.prototype.globalAgent = https.globalAgent;
10-
HttpsWrap.prototype.Server = https.Server;
11-
HttpsWrap.prototype.createServer = https.createServer;
10+
11+
// Server functionality needs to be disabled.
12+
//HttpsWrap.prototype.Server = https.Server;
13+
//HttpsWrap.prototype.createServer = https.createServer;
1214

1315
HttpsWrap.prototype.request = function (options, cb) {
1416
if (_accessControl) {

src/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ set(CMAKE_CXX_STANDARD 11)
66
option(WINDOWLESS_EGL "WINDOWLESS_EGL" OFF)
77
option(PXCORE_WAYLAND_EGL "PXCORE_WAYLAND_EGL" OFF)
88
option(PXCORE_MATRIX_HELPERS "PXCORE_MATRIX_HELPERS" ON)
9+
option(PXCORE_ACCESS_CONTROL_CHECK "PXCORE_ACCESS_CONTROL_CHECK" ON)
910
option(BUILD_RTCORE_LIBS "BUILD_RTCORE_LIBS" ON)
1011
option(BUILD_PXCORE_LIBS "BUILD_PXCORE_LIBS" ON)
1112
option(OUTPUT_LIBS_LOCAL "OUTPUT_LIBS_LOCAL" OFF)
@@ -201,3 +202,7 @@ if (BUILD_RTCORE_LIBS)
201202
add_library(rtCore SHARED ${RTCORE_FILES})
202203
endif(BUILD_RTCORE_SHARED_LIBRARY GREATER 0)
203204
endif (BUILD_RTCORE_LIBS)
205+
206+
if (PXCORE_ACCESS_CONTROL_CHECK)
207+
add_definitions(-DENABLE_ACCESS_CONTROL_CHECK)
208+
endif (PXCORE_ACCESS_CONTROL_CHECK)

src/rtFileDownloader.cpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,9 @@ bool rtFileDownloader::downloadFromNetwork(rtFileDownloadRequest* downloadReques
655655
if (false == headerOnly)
656656
{
657657
downloadRequest->setDownloadedData(chunk.contentsBuffer, chunk.contentsSize);
658+
#ifdef ENABLE_ACCESS_CONTROL_CHECK
659+
checkAccessControlHeaders(downloadRequest);
660+
#endif
658661
}
659662
else if (chunk.contentsBuffer != NULL)
660663
{
@@ -789,3 +792,123 @@ void rtFileDownloader::checkForExpiredHandles()
789792
downloadHandleMutex.unlock();
790793
}
791794

795+
void rtFileDownloader::checkAccessControlHeaders(rtFileDownloadRequest* downloadRequest)
796+
{
797+
rtLogDebug("checkAccessControlHeaders");
798+
799+
rtString origin;
800+
const char* originFieldStart = "origin:";
801+
const std::vector<rtString>& headers = downloadRequest->additionalHttpHeaders();
802+
for (std::vector<rtString>::const_iterator it = headers.begin(); it != headers.end(); it++)
803+
{
804+
const rtString& header = (*it);
805+
if (!header.isEmpty())
806+
{
807+
rtLogDebug("request header: '%s'", header.cString());
808+
809+
// Case-insensitive compare.
810+
const char* h = header.cString();
811+
const char* o = originFieldStart;
812+
size_t start = 0;
813+
for (; *h && *o && tolower(*h) == *o; h++, o++, start++);
814+
if (*o == 0)
815+
{
816+
// Trim left.
817+
for (; *h == ' ' || *h == '\t'; h++, start++);
818+
if (*h != 0)
819+
origin = header.substring(start);
820+
break;
821+
}
822+
}
823+
}
824+
if (origin.isEmpty())
825+
{
826+
// Did not send Origin, do not check anything.
827+
rtLogDebug("no origin in request headers");
828+
return;
829+
}
830+
831+
rtLogDebug("origin in request is: '%s'", origin.cString());
832+
rtString reqUrl = downloadRequest->fileUrl();
833+
if (reqUrl.isEmpty() ||
834+
reqUrl.beginsWith(origin.cString()))
835+
{
836+
// Don't expect CORS response headers on same-origin requests.
837+
rtLogDebug("request is same-origin: '%s'", reqUrl.cString());
838+
return;
839+
}
840+
841+
rtString allowOrigin;
842+
const char* allowOriginFieldStart = "access-control-allow-origin:";
843+
rtString resHeaders(downloadRequest->headerData(), downloadRequest->headerDataSize());
844+
if (!resHeaders.isEmpty())
845+
{
846+
rtLogDebug("response headers are: '%s'", resHeaders.cString());
847+
848+
// Case-insensitive search.
849+
const char* h = resHeaders.cString();
850+
const char* o = allowOriginFieldStart;
851+
size_t start = 0;
852+
for (; *h && *o && tolower(*h) == *o; h++, o++, start++);
853+
if (*o != 0)
854+
{
855+
allowOriginFieldStart = "\r\naccess-control-allow-origin:";
856+
o = allowOriginFieldStart;
857+
for (; *h && *o; h++, start++)
858+
o = tolower(*h) == *o ? o + 1 : allowOriginFieldStart;
859+
}
860+
if (*o == 0)
861+
{
862+
// Trim left.
863+
for (; *h == ' ' || *h == '\t'; h++, start++);
864+
if (*h != 0)
865+
{
866+
size_t end = resHeaders.find(start, "\r\n");
867+
end = end >= start ? end : 0;
868+
allowOrigin = resHeaders.substring(start, end - start);
869+
}
870+
}
871+
}
872+
if (!allowOrigin.isEmpty())
873+
{
874+
rtLogDebug("allow-origin in response is: '%s'", allowOrigin.cString());
875+
876+
// If the value of Access-Control-Allow-Origin is not a case-sensitive match, return fail.
877+
if (allowOrigin.compare("*") == 0 ||
878+
allowOrigin.compare(origin.cString()) == 0)
879+
{
880+
// Allow access to the resource's contents.
881+
rtLogDebug("allow access for origin '%s'", origin.cString());
882+
return;
883+
}
884+
}
885+
886+
// Disallow access to the resource's contents.
887+
if (downloadRequest->downloadedData() != NULL)
888+
{
889+
free(downloadRequest->downloadedData());
890+
}
891+
downloadRequest->setDownloadedData(NULL, 0);
892+
stringstream errorStream;
893+
// If the response includes zero Access-Control-Allow-Origin header values, return fail.
894+
if (allowOrigin.isEmpty())
895+
{
896+
errorStream << "No 'Access-Control-Allow-Origin' header is present on the requested resource.";
897+
errorStream << " Origin '";
898+
errorStream << origin.cString();
899+
errorStream << "' is therefore not allowed access";
900+
}
901+
else
902+
{
903+
errorStream << "The 'Access-Control-Allow-Origin' header has a value '";
904+
errorStream << allowOrigin.cString();
905+
errorStream << "' that is not equal to the supplied origin.";
906+
errorStream << " Origin '";
907+
errorStream << origin.cString();
908+
errorStream << "' is therefore not allowed access";
909+
}
910+
const std::string& errorStr = errorStream.str();
911+
rtLogWarn("disallow access for origin '%s' because: %s", origin.cString(), errorStr.c_str());
912+
downloadRequest->setDownloadStatusCode(-1);
913+
downloadRequest->setErrorString(errorStr.c_str());
914+
}

src/rtFileDownloader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ class rtFileDownloader
146146
#endif
147147
CURL* retrieveDownloadHandle();
148148
void releaseDownloadHandle(CURL* curlHandle, int expiresTime);
149+
static void checkAccessControlHeaders(rtFileDownloadRequest* downloadRequest);
149150
//todo: hash mPendingDownloadRequests;
150151
//todo: string list mPendingDownloadOrderList;
151152
//todo: list mActiveDownloads;

src/rtUrlUtils.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "rtUrlUtils.h"
2222
#include <string.h>
23+
#include <ctype.h>
2324

2425

2526
#if !defined(WIN32) && !defined(ENABLE_DFB)
@@ -85,3 +86,31 @@ rtString rtUrlEncodeParameters(const char* url)
8586
// printf("Returning %s\n",retVal.cString());
8687
return retVal;
8788
}
89+
90+
/*
91+
* rtUrlGetOrigin: Takes an url in the form of
92+
* "http://blahblah/index.js?some=some1&parm=value" and
93+
* returns an rtString in form scheme://host or
94+
* an empty rtString if url is not http:// or https://
95+
*/
96+
rtString rtUrlGetOrigin(const char* url)
97+
{
98+
if (url != NULL)
99+
{
100+
// See http://www.ietf.org/rfc/rfc3986.txt.
101+
const char* u = url;
102+
const char* s = "http";
103+
for (; *u && *s && tolower(*u) == *s; u++, s++);
104+
if (*s == 0)
105+
{
106+
for (s = "s"; *u && *s && tolower(*u) == *s; u++, s++);
107+
for (s = "://"; *u && *s && *u == *s; u++, s++);
108+
if (*s == 0)
109+
{
110+
for (; *u && *u != '/' && *u != '?' && *u != '#'; u++);
111+
return rtString(url, u - url);
112+
}
113+
}
114+
}
115+
return rtString();
116+
}

src/rtUrlUtils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@
2222
#include "rtString.h"
2323

2424
rtString rtUrlEncodeParameters(const char* url);
25+
rtString rtUrlGetOrigin(const char* url);
2526

2627
#endif

0 commit comments

Comments
 (0)