Logo ROOT  
Reference Guide
rooturlschemehandler.cpp
Go to the documentation of this file.
1// Author: Sergey Linev <S.Linev@gsi.de>
2// Date: 2017-06-29
3// Warning: This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is welcome!
4
5
6/*************************************************************************
7 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
8 * All rights reserved. *
9 * *
10 * For the licensing terms see $ROOTSYS/LICENSE. *
11 * For the list of contributors see $ROOTSYS/README/CREDITS. *
12 *************************************************************************/
13
14
16
17#include "rootwebpage.h" // only because of logger channel
18#include <cstring>
19
20#include <QBuffer>
21#include <QByteArray>
22#include <QFile>
23#include <QWebEngineUrlRequestJob>
24
25#include <ROOT/RLogger.hxx>
26
27#include "THttpServer.h"
28#include "THttpCallArg.h"
29#include "TBase64.h"
30
31/** \class UrlRequestJobHolder
32\ingroup qt5webdisplay
33
34Class UrlRequestJobHolder
35Required to monitor state of QWebEngineUrlRequestJob
36Qt can delete object at any time, therefore one connects destroy signal
37from the request to clear pointer
38
39*/
40
41/////////////////////////////////////////////////////////////////
42/// Constructor
43
44UrlRequestJobHolder::UrlRequestJobHolder(QWebEngineUrlRequestJob *req) : QObject(), fRequest(req)
45{
46 if (fRequest)
47 connect(fRequest, &QObject::destroyed, this, &UrlRequestJobHolder::onRequestDeleted);
48}
49
50/////////////////////////////////////////////////////////////////
51/// destroyed signal handler
52
54{
55 if (fRequest == obj)
56 fRequest = nullptr;
57}
58
59/////////////////////////////////////////////////////////////////
60/// Reset holder
61
63{
64 if (fRequest)
65 disconnect(fRequest, &QObject::destroyed, this, &UrlRequestJobHolder::onRequestDeleted);
66 fRequest = nullptr;
67}
68
69// ===================================================================
70
71/////////////////////////////////////////////////////////////////////////////////////
72/// Class TWebGuiCallArg
73/// Specialized handler of requests in THttpServer with QWebEngine
74////////////////////////////////////////////////////////////////////////////////////
75
77
78protected:
80
81public:
82 explicit TWebGuiCallArg(QWebEngineUrlRequestJob *req = nullptr) : THttpCallArg(), fRequest(req) {}
83
84 virtual ~TWebGuiCallArg() {}
85
86 /** provide WS kind */
87 const char *GetWSKind() const override { return "rawlongpoll"; }
88
89 /** provide WS platform, intentionally keep qt5 here while it only used on client side */
90 const char *GetWSPlatform() const override { return "qt5"; }
91
92 void SendFile(const char *fname)
93 {
94 const char *mime = THttpServer::GetMimeType(fname);
95
96 QBuffer *buffer = new QBuffer;
97
98 QFile file(fname);
99 buffer->open(QIODevice::WriteOnly);
100 if (file.open(QIODevice::ReadOnly)) {
101 QByteArray arr = file.readAll();
102 if (strstr(fname, ".mjs") && !strcmp(mime, "text/javascript")) {
103 const char *mark1 = "///_begin_exclude_in_qt5web_", *mark2 = "///_end_exclude_in_qt5web_";
104 auto p1 = arr.indexOf(mark1);
105 auto p2 = arr.indexOf(mark2);
106 if ((p1 > 0) && (p2 > p1)) arr.remove(p1, p2 - p1 + strlen(mark2));
107 }
108
109 buffer->write(arr);
110 }
111 file.close();
112 buffer->close();
113
114 QWebEngineUrlRequestJob *req = fRequest.req();
115
116 if (req) {
117 buffer->connect(req, &QObject::destroyed, buffer, &QObject::deleteLater);
118 req->reply(mime, buffer);
119 fRequest.reset();
120 } else {
121 delete buffer;
122 }
123 }
124
125 void HttpReplied() override
126 {
127 QWebEngineUrlRequestJob *req = fRequest.req();
128
129 if (!req) {
130 R__LOG_ERROR(QtWebDisplayLog()) << "Qt5 request already processed path " << GetPathName() << " file " << GetFileName();
131 return;
132 }
133
134 if (Is404()) {
135 R__LOG_ERROR(QtWebDisplayLog()) << "Qt5 request FAIL path " << GetPathName() << " file " << GetFileName();
136
137 req->fail(QWebEngineUrlRequestJob::UrlNotFound);
138 // abort request
139 } else if (IsFile()) {
140 // send file
141 SendFile((const char *)GetContent());
142 } else {
143
144 QBuffer *buffer = new QBuffer;
145
146 buffer->open(QIODevice::WriteOnly);
147
148 buffer->write((const char *)GetContent(), GetContentLength());
149
150 buffer->close();
151
152 buffer->connect(req, &QObject::destroyed, buffer, &QObject::deleteLater);
153
154 req->reply(GetContentType(), buffer);
155 }
156
157 fRequest.reset();
158 }
159};
160
161
162/////////////////////////////////////////////////////////////////
163/// Returns fully qualified URL, required to open in QWindow
164
165QString RootUrlSchemeHandler::MakeFullUrl(THttpServer *serv, const QString &url)
166{
167 // TODO: provide support for many servers
168 fServer = serv;
169
170 QString res = "rootscheme://root.server1";
171 res.append(url);
172 return res;
173}
174
175
176///////////////////////////////////////////////////////////////////////////////////////////////////
177/// Start processing of emulated HTTP request in WebEngine scheme handler
178/// Either one reads file or redirect request to THttpServer
179
180void RootUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *request)
181{
182 QUrl url = request->requestUrl();
183
184 if (!fServer) {
185 R__LOG_ERROR(QtWebDisplayLog()) << "Server not specified when request is started";
186 request->fail(QWebEngineUrlRequestJob::UrlNotFound);
187 return;
188 }
189
190 QString inp_path = url.path();
191 QString inp_query = url.query();
192 QString inp_method = request->requestMethod();
193
194 // printf("REQUEST PATH:%s QUERY:%s\n", inp_path.toLatin1().data(), inp_query.toLatin1().data());
195
196 auto arg = std::make_shared<TWebGuiCallArg>(request);
197
198 TString fname;
199
200 // process file
201 if (fServer->IsFileRequested(inp_path.toLatin1().data(), fname)) {
202 arg->SendFile(fname.Data());
203 return;
204 }
205
206 // Analyze and cut post data as soon as possible
207 TString query = inp_query.toLatin1().data();
208 Int_t pos = query.Index("&post=");
209 if (pos != kNPOS) {
210 TString buf = TBase64::Decode(query.Data() + pos + 6);
211 arg->SetPostData(std::string(buf.Data()));
212 query.Resize(pos);
213 }
214
215 arg->SetPathAndFileName(inp_path.toLatin1().data());
216 arg->SetQuery(query.Data());
217 arg->SetMethod(inp_method.toLatin1().data());
218 arg->SetTopName("webgui");
219
220 // can process immediately - function called in main thread
221 fServer->SubmitHttp(arg, kTRUE);
222}
#define R__LOG_ERROR(...)
Definition: RLogger.hxx:362
const Ssiz_t kNPOS
Definition: RtypesCore.h:124
const Bool_t kTRUE
Definition: RtypesCore.h:100
THttpServer * fServer
server instance which should handle requests
QString MakeFullUrl(THttpServer *serv, const QString &url)
Returns fully qualified URL, required to open in QWindow.
void requestStarted(QWebEngineUrlRequestJob *request) override
Start processing of emulated HTTP request in WebEngine scheme handler Either one reads file or redire...
static TString Decode(const char *data)
Decode a base64 string date into a generic TString.
Definition: TBase64.cxx:131
Long_t GetContentLength() const
Definition: THttpCallArg.h:239
Bool_t Is404() const
Definition: THttpCallArg.h:231
const void * GetContent() const
Definition: THttpCallArg.h:240
const char * GetPathName() const
returns path name from request URL
Definition: THttpCallArg.h:146
const char * GetContentType() const
Definition: THttpCallArg.h:229
const char * GetFileName() const
returns file name from request URL
Definition: THttpCallArg.h:149
Bool_t IsFile() const
Definition: THttpCallArg.h:232
Bool_t IsFileRequested(const char *uri, TString &res) const
Check if file is requested, thread safe.
Bool_t SubmitHttp(std::shared_ptr< THttpCallArg > arg, Bool_t can_run_immediately=kFALSE)
Submit HTTP request.
static const char * GetMimeType(const char *path)
Guess mime type base on file extension.
Basic string class.
Definition: TString.h:136
const char * Data() const
Definition: TString.h:369
void Resize(Ssiz_t n)
Resize the string. Truncate or add blanks as necessary.
Definition: TString.cxx:1120
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition: TString.h:639
Class TWebGuiCallArg Specialized handler of requests in THttpServer with QWebEngine.
UrlRequestJobHolder fRequest
TWebGuiCallArg(QWebEngineUrlRequestJob *req=nullptr)
void SendFile(const char *fname)
void HttpReplied() override
virtual method to inform object that http request is processed Normally condition is notified and wai...
const char * GetWSKind() const override
provide WS kind
const char * GetWSPlatform() const override
provide WS platform, intentionally keep qt5 here while it only used on client side
Class UrlRequestJobHolder Required to monitor state of QWebEngineUrlRequestJob Qt can delete object a...
QWebEngineUrlRequestJob * req() const
void onRequestDeleted(QObject *obj)
destroyed signal handler
void reset()
Reset holder.
UrlRequestJobHolder(QWebEngineUrlRequestJob *req)
Constructor.
QWebEngineUrlRequestJob * fRequest
Definition: file.py:1
ROOT::Experimental::RLogChannel & QtWebDisplayLog()
Definition: rootwebpage.cpp:20