2018-10-25 08:34:21 -05:00
/////////////////////////////////////////////////////////////////////////////////
//
2019-01-09 08:21:38 -06:00
// Copyright (C) 2018- Equinor ASA
2018-10-25 08:34:21 -05:00
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
# include "RicHoloLensRestClient.h"
# include "cvfBase.h"
# include "cvfTrace.h"
# include <QNetworkRequest>
# include <QSslConfiguration>
2018-12-18 15:08:57 -06:00
# include <QDateTime>
// For getting time stamps for round-trip timing
# if defined (__linux__)
# include <sys/time.h>
# include <ctime>
# endif
2018-10-25 08:34:21 -05:00
# ifndef QT_NO_OPENSSL
// Uncomment to enable experimental SSL support
// The experimental support must be revised before shipping
# define EXPERIMENTAL_SSL_SUPPORT
# endif
//==================================================================================================
//
//
//
//==================================================================================================
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RicHoloLensRestClient : : RicHoloLensRestClient ( QString serverUrl , QString sessionName , RicHoloLensRestResponseHandler * responseHandler )
: m_serverUrl ( serverUrl ) ,
m_sessionName ( sessionName ) ,
2019-01-15 06:23:12 -06:00
m_responseHandler ( responseHandler ) ,
m_dbgDisableCertificateVerification ( false )
2018-10-25 08:34:21 -05:00
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensRestClient : : clearResponseHandler ( )
{
m_responseHandler = nullptr ;
}
2019-01-15 06:23:12 -06:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensRestClient : : dbgDisableCertificateVerification ( )
{
m_dbgDisableCertificateVerification = true ;
}
2018-10-25 08:34:21 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-12-18 15:08:57 -06:00
void RicHoloLensRestClient : : createSession ( const QByteArray & sessionPinCode )
2018-10-25 08:34:21 -05:00
{
const QString url = m_serverUrl + " /sessions/create/ " + m_sessionName ;
cvf : : Trace : : show ( " createSession: POST on url: %s " , url . toLatin1 ( ) . constData ( ) ) ;
QNetworkRequest request ( url ) ;
//request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
request . setHeader ( QNetworkRequest : : ContentTypeHeader , QVariant ( " application/x-www-form-urlencoded " ) ) ;
2018-12-18 15:08:57 -06:00
request . setRawHeader ( " PinCode " , sessionPinCode ) ;
2018-10-25 08:34:21 -05:00
2019-01-15 03:01:01 -06:00
2018-10-25 08:34:21 -05:00
# ifdef EXPERIMENTAL_SSL_SUPPORT
// NOTE !!!
// Apparently something like this is currently needed in order to get SSL/HTTPS going
// Still, can't quite figure it out since it appears to be sufficient to do this on the first request
2019-01-15 06:23:12 -06:00
// This will have to be investigated further, SP 20180924
2018-10-25 08:34:21 -05:00
QSslConfiguration sslConf = request . sslConfiguration ( ) ;
// Needed this one to be able to connect to sharing server
sslConf . setProtocol ( QSsl : : AnyProtocol ) ;
2019-01-15 06:23:12 -06:00
if ( m_dbgDisableCertificateVerification )
2019-01-15 03:01:01 -06:00
{
sslConf . setPeerVerifyMode ( QSslSocket : : VerifyNone ) ;
}
2018-10-25 08:34:21 -05:00
request . setSslConfiguration ( sslConf ) ;
# endif
QNetworkReply * reply = m_accessManager . post ( request , QByteArray ( ) ) ;
connect ( reply , SIGNAL ( finished ( ) ) , SLOT ( slotCreateSessionFinished ( ) ) ) ;
# ifdef EXPERIMENTAL_SSL_SUPPORT
connect ( reply , SIGNAL ( sslErrors ( const QList < QSslError > & ) ) , SLOT ( slotSslErrors ( const QList < QSslError > & ) ) ) ;
# endif
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensRestClient : : slotCreateSessionFinished ( )
{
QNetworkReply * reply = dynamic_cast < QNetworkReply * > ( sender ( ) ) ;
if ( ! reply )
{
return ;
}
if ( detectAndHandleErrorReply ( " createSession " , reply ) )
{
reply - > deleteLater ( ) ;
2018-12-18 15:08:57 -06:00
m_responseHandler - > handleFailedCreateSession ( ) ;
2018-10-25 08:34:21 -05:00
return ;
}
2018-12-18 15:08:57 -06:00
const QByteArray serverData = reply - > readAll ( ) ;
//cvf::Trace::show(" serverResponse: %s", serverData.constData());
// Currently we get the bearer token back in the response wholesale
// The format we get is typically: "Bearer <the token>"
// For example: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
// Presumably the format of the response will change, but for now just strip away the starting "Bearer " string and consider the rest as the actual token
m_bearerToken = serverData ;
m_bearerToken . replace ( " Bearer " , " " ) ;
2018-10-25 08:34:21 -05:00
reply - > deleteLater ( ) ;
cvf : : Trace : : show ( " createSession OK " ) ;
if ( m_responseHandler )
{
m_responseHandler - > handleSuccessfulCreateSession ( ) ;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensRestClient : : deleteSession ( )
{
const QString url = m_serverUrl + " /sessions/delete/ " + m_sessionName ;
cvf : : Trace : : show ( " deleteSession: DELETE on url: %s " , url . toLatin1 ( ) . constData ( ) ) ;
QNetworkRequest request ( url ) ;
//request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
request . setHeader ( QNetworkRequest : : ContentTypeHeader , QVariant ( " application/x-www-form-urlencoded " ) ) ;
2018-12-18 15:08:57 -06:00
addBearerAuthenticationHeaderToRequest ( & request ) ;
2018-10-25 08:34:21 -05:00
QNetworkReply * reply = m_accessManager . deleteResource ( request ) ;
connect ( reply , SIGNAL ( finished ( ) ) , SLOT ( slotDeleteSessionFinished ( ) ) ) ;
# ifdef EXPERIMENTAL_SSL_SUPPORT
connect ( reply , SIGNAL ( sslErrors ( const QList < QSslError > & ) ) , SLOT ( slotSslErrors ( const QList < QSslError > & ) ) ) ;
# endif
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensRestClient : : slotDeleteSessionFinished ( )
{
QNetworkReply * reply = dynamic_cast < QNetworkReply * > ( sender ( ) ) ;
if ( ! reply )
{
return ;
}
if ( detectAndHandleErrorReply ( " deleteSession " , reply ) )
{
reply - > deleteLater ( ) ;
return ;
}
reply - > deleteLater ( ) ;
cvf : : Trace : : show ( " deleteSession OK " ) ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensRestClient : : sendMetaData ( int metaDataSequenceNumber , const QString & jsonMetaDataString )
{
const QString url = m_serverUrl + " /sessions/ " + m_sessionName + " /metadata " ;
cvf : : Trace : : show ( " sendMetaData (metaDataSequenceNumber=%d): POST on url: %s " , metaDataSequenceNumber , url . toLatin1 ( ) . constData ( ) ) ;
2018-12-18 15:08:57 -06:00
const qint64 sendStartTimeStamp_ms = getCurrentTimeStamp_ms ( ) ;
2018-10-25 08:34:21 -05:00
QNetworkRequest request ( url ) ;
request . setHeader ( QNetworkRequest : : ContentTypeHeader , QVariant ( " application/json " ) ) ;
2018-12-18 15:08:57 -06:00
addBearerAuthenticationHeaderToRequest ( & request ) ;
2018-10-25 08:34:21 -05:00
const QByteArray jsonByteArr = jsonMetaDataString . toLatin1 ( ) ;
QNetworkReply * reply = m_accessManager . post ( request , jsonByteArr ) ;
2018-12-18 15:08:57 -06:00
reply - > setProperty ( " holo_metaDataSequenceNumber " , QVariant ( metaDataSequenceNumber ) ) ;
reply - > setProperty ( " holo_sendStartTimeStamp_ms " , QVariant ( sendStartTimeStamp_ms ) ) ;
2018-10-25 08:34:21 -05:00
connect ( reply , SIGNAL ( finished ( ) ) , SLOT ( slotSendMetaDataFinished ( ) ) ) ;
# ifdef EXPERIMENTAL_SSL_SUPPORT
connect ( reply , SIGNAL ( sslErrors ( const QList < QSslError > & ) ) , SLOT ( slotSslErrors ( const QList < QSslError > & ) ) ) ;
# endif
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensRestClient : : slotSendMetaDataFinished ( )
{
QNetworkReply * reply = dynamic_cast < QNetworkReply * > ( sender ( ) ) ;
if ( ! reply )
{
return ;
}
if ( detectAndHandleErrorReply ( " sendMetaData " , reply ) )
{
reply - > deleteLater ( ) ;
return ;
}
int metaDataSequenceNumber = - 1 ;
{
2018-12-18 15:08:57 -06:00
QVariant var = reply - > property ( " holo_metaDataSequenceNumber " ) ;
2018-10-25 08:34:21 -05:00
if ( var . type ( ) = = QVariant : : Int )
{
metaDataSequenceNumber = var . toInt ( ) ;
}
}
2018-12-18 15:08:57 -06:00
double elapsedTime_s = - 1 ;
{
QVariant var = reply - > property ( " holo_sendStartTimeStamp_ms " ) ;
if ( var . type ( ) = = QVariant : : LongLong )
{
const qint64 startTimeStamp_ms = var . toLongLong ( ) ;
elapsedTime_s = ( getCurrentTimeStamp_ms ( ) - startTimeStamp_ms ) / 1000.0 ;
}
}
const QByteArray serverData = reply - > readAll ( ) ;
//cvf::Trace::show(" serverResponse: %s", serverData.constData());
2018-10-25 08:34:21 -05:00
reply - > deleteLater ( ) ;
2018-12-18 15:08:57 -06:00
cvf : : Trace : : show ( " sendMetaData (metaDataSequenceNumber=%d) OK, elapsedTime=%.2fs " , metaDataSequenceNumber , elapsedTime_s ) ;
2018-10-25 08:34:21 -05:00
if ( m_responseHandler )
{
2018-12-18 15:08:57 -06:00
m_responseHandler - > handleSuccessfulSendMetaData ( metaDataSequenceNumber , serverData ) ;
2018-10-25 08:34:21 -05:00
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2019-01-17 02:37:35 -06:00
void RicHoloLensRestClient : : sendBinaryData ( const QByteArray & binaryDataArr , QByteArray dbgTagString )
2018-10-25 08:34:21 -05:00
{
const QString url = m_serverUrl + " /sessions/ " + m_sessionName + " /data " ;
2019-01-17 02:37:35 -06:00
cvf : : Trace : : show ( " sendBinaryData(%s): POST on url: %s " , dbgTagString . constData ( ) , url . toLatin1 ( ) . constData ( ) ) ;
2018-10-25 08:34:21 -05:00
2018-12-18 15:08:57 -06:00
const qint64 sendStartTimeStamp_ms = getCurrentTimeStamp_ms ( ) ;
2018-10-25 08:34:21 -05:00
QNetworkRequest request ( url ) ;
request . setHeader ( QNetworkRequest : : ContentTypeHeader , QVariant ( " application/octet-stream " ) ) ;
2018-12-18 15:08:57 -06:00
addBearerAuthenticationHeaderToRequest ( & request ) ;
2018-10-25 08:34:21 -05:00
QNetworkReply * reply = m_accessManager . post ( request , binaryDataArr ) ;
2018-12-18 15:08:57 -06:00
reply - > setProperty ( " holo_sendStartTimeStamp_ms " , QVariant ( sendStartTimeStamp_ms ) ) ;
2019-01-17 02:37:35 -06:00
reply - > setProperty ( " holo_dbgTagString " , QVariant ( dbgTagString ) ) ;
2018-12-18 15:08:57 -06:00
2018-10-25 08:34:21 -05:00
connect ( reply , SIGNAL ( finished ( ) ) , SLOT ( slotSendBinaryDataFinished ( ) ) ) ;
2018-12-18 15:08:57 -06:00
// Debugging!
connect ( reply , SIGNAL ( uploadProgress ( qint64 , qint64 ) ) , SLOT ( slotDbgUploadProgress ( qint64 , qint64 ) ) ) ;
2018-10-25 08:34:21 -05:00
# ifdef EXPERIMENTAL_SSL_SUPPORT
connect ( reply , SIGNAL ( sslErrors ( const QList < QSslError > & ) ) , SLOT ( slotSslErrors ( const QList < QSslError > & ) ) ) ;
# endif
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensRestClient : : slotSendBinaryDataFinished ( )
{
QNetworkReply * reply = dynamic_cast < QNetworkReply * > ( sender ( ) ) ;
if ( ! reply )
{
return ;
}
if ( detectAndHandleErrorReply ( " sendBinaryData " , reply ) )
{
reply - > deleteLater ( ) ;
return ;
}
2018-12-18 15:08:57 -06:00
double elapsedTime_s = - 1 ;
{
QVariant var = reply - > property ( " holo_sendStartTimeStamp_ms " ) ;
if ( var . type ( ) = = QVariant : : LongLong )
{
const qint64 startTimeStamp_ms = var . toLongLong ( ) ;
elapsedTime_s = ( getCurrentTimeStamp_ms ( ) - startTimeStamp_ms ) / 1000.0 ;
}
}
2019-01-17 02:37:35 -06:00
QByteArray dbgTagString ;
{
QVariant var = reply - > property ( " holo_dbgTagString " ) ;
if ( var . type ( ) = = QVariant : : ByteArray )
{
dbgTagString = var . toByteArray ( ) ;
}
}
2018-10-25 08:34:21 -05:00
reply - > deleteLater ( ) ;
2019-01-17 02:37:35 -06:00
cvf : : Trace : : show ( " sendBinaryData(%s) OK, elapsedTime=%.2fs " , dbgTagString . constData ( ) , elapsedTime_s ) ;
2018-12-18 15:08:57 -06:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensRestClient : : slotDbgUploadProgress ( qint64 bytesSent , qint64 bytesTotal )
{
static int sl_lastPct = - 1 ;
int pct = 0 ;
if ( bytesTotal > 0 )
{
pct = static_cast < int > ( 100 * ( bytesSent / static_cast < double > ( bytesTotal ) ) ) ;
}
if ( pct % 10 = = 0 & & pct ! = sl_lastPct )
{
double elapsedTime_s = - 1 ;
2019-01-17 02:37:35 -06:00
QByteArray dbgTagString ;
2018-12-18 15:08:57 -06:00
QNetworkReply * reply = dynamic_cast < QNetworkReply * > ( sender ( ) ) ;
if ( reply )
{
{
2019-01-17 02:37:35 -06:00
QVariant var = reply - > property ( " holo_sendStartTimeStamp_ms " ) ;
if ( var . type ( ) = = QVariant : : LongLong )
{
const qint64 startTimeStamp_ms = var . toLongLong ( ) ;
elapsedTime_s = ( getCurrentTimeStamp_ms ( ) - startTimeStamp_ms ) / 1000.0 ;
}
}
{
QVariant var = reply - > property ( " holo_dbgTagString " ) ;
if ( var . type ( ) = = QVariant : : ByteArray )
{
dbgTagString = var . toByteArray ( ) ;
}
2018-12-18 15:08:57 -06:00
}
}
2019-01-17 02:37:35 -06:00
cvf : : Trace : : show ( " Progress sendBinaryData(%s): %3d%%, %.2f/%.2fMB (elapsedTime=%.2fs) " , dbgTagString . constData ( ) , pct , bytesSent / ( 1024.0 * 1024.0 ) , bytesTotal / ( 1024.0 * 1024.0 ) , elapsedTime_s ) ;
2018-12-18 15:08:57 -06:00
sl_lastPct = pct ;
}
2018-10-25 08:34:21 -05:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensRestClient : : slotSslErrors ( const QList < QSslError > & errors )
{
# ifdef EXPERIMENTAL_SSL_SUPPORT
cvf : : Trace : : show ( " RicHoloLensRestClient::slotSslErrors() " ) ;
for ( int i = 0 ; i < errors . size ( ) ; i + + )
{
cvf : : Trace : : show ( " %s " , errors [ i ] . errorString ( ) . toLatin1 ( ) . constData ( ) ) ;
}
# endif
}
2018-12-18 15:08:57 -06:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicHoloLensRestClient : : addBearerAuthenticationHeaderToRequest ( QNetworkRequest * request ) const
{
CVF_ASSERT ( request ) ;
request - > setRawHeader ( " Authorization " , " Bearer " + m_bearerToken ) ;
}
2018-10-25 08:34:21 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicHoloLensRestClient : : detectAndHandleErrorReply ( QString operationName , QNetworkReply * reply )
{
CVF_ASSERT ( reply ) ;
const QNetworkReply : : NetworkError nwErrCode = reply - > error ( ) ;
const int httpStatusCode = reply - > attribute ( QNetworkRequest : : HttpStatusCodeAttribute ) . toInt ( ) ;
if ( nwErrCode = = QNetworkReply : : NoError & & httpStatusCode = = 200 )
{
// No error detected
return false ;
}
QString mainErrMsg = operationName + " FAILED " ;
if ( nwErrCode ! = QNetworkReply : : NoError )
{
const QString nwErrCodeAsString = networkErrorCodeAsString ( nwErrCode ) ;
const QString errText = reply - > errorString ( ) ;
mainErrMsg + = QString ( " [nwErr='%1'(%2) httpStatus=%3]: %4 " ) . arg ( nwErrCodeAsString ) . arg ( nwErrCode ) . arg ( httpStatusCode ) . arg ( errText ) ;
}
else
{
mainErrMsg + = QString ( " [httpStatus=%1] " ) . arg ( httpStatusCode ) ;
}
cvf : : Trace : : show ( mainErrMsg . toLatin1 ( ) . constData ( ) ) ;
reply - > errorString ( ) ;
const QString url = reply - > url ( ) . toString ( ) ;
cvf : : Trace : : show ( " url: %s " , url . toLatin1 ( ) . constData ( ) ) ;
const QByteArray serverData = reply - > readAll ( ) ;
cvf : : Trace : : show ( " serverResponse: %s " , serverData . constData ( ) ) ;
if ( m_responseHandler )
{
m_responseHandler - > handleError ( mainErrMsg , url , serverData ) ;
}
return true ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RicHoloLensRestClient : : networkErrorCodeAsString ( QNetworkReply : : NetworkError nwErr )
{
switch ( nwErr )
{
case QNetworkReply : : NoError : return " NoError " ;
case QNetworkReply : : ConnectionRefusedError : return " ConnectionRefusedError " ;
case QNetworkReply : : RemoteHostClosedError : return " RemoteHostClosedError " ;
case QNetworkReply : : HostNotFoundError : return " HostNotFoundError " ;
case QNetworkReply : : TimeoutError : return " TimeoutError " ;
case QNetworkReply : : OperationCanceledError : return " OperationCanceledError " ;
case QNetworkReply : : SslHandshakeFailedError : return " SslHandshakeFailedError " ;
//case QNetworkReply::TemporaryNetworkFailureError: return "TemporaryNetworkFailureError";
case QNetworkReply : : UnknownNetworkError : return " UnknownNetworkError " ;
case QNetworkReply : : ProxyConnectionRefusedError : return " ProxyConnectionRefusedError " ;
case QNetworkReply : : ProxyConnectionClosedError : return " ProxyConnectionClosedError " ;
case QNetworkReply : : ProxyNotFoundError : return " ProxyNotFoundError " ;
case QNetworkReply : : ProxyTimeoutError : return " ProxyTimeoutError " ;
case QNetworkReply : : ProxyAuthenticationRequiredError : return " ProxyAuthenticationRequiredError " ;
case QNetworkReply : : UnknownProxyError : return " UnknownProxyError " ;
case QNetworkReply : : ContentAccessDenied : return " ContentAccessDenied " ;
case QNetworkReply : : ContentOperationNotPermittedError : return " ContentOperationNotPermittedError " ;
case QNetworkReply : : ContentNotFoundError : return " ContentNotFoundError " ;
case QNetworkReply : : AuthenticationRequiredError : return " AuthenticationRequiredError " ;
case QNetworkReply : : ContentReSendError : return " ContentReSendError " ;
case QNetworkReply : : UnknownContentError : return " UnknownContentError " ;
case QNetworkReply : : ProtocolUnknownError : return " ProtocolUnknownError " ;
case QNetworkReply : : ProtocolInvalidOperationError : return " ProtocolInvalidOperationError " ;
case QNetworkReply : : ProtocolFailure : return " ProtocolFailure " ;
} ;
return " UnknownErrorCode " ;
}
2018-12-18 15:08:57 -06:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
qint64 RicHoloLensRestClient : : getCurrentTimeStamp_ms ( )
{
# if QT_VERSION >= 0x040700
const qint64 timeStamp_ms = QDateTime : : currentMSecsSinceEpoch ( ) ;
return timeStamp_ms ;
# elif defined(__linux__)
struct timespec ts ;
if ( clock_gettime ( CLOCK_MONOTONIC , & ts ) = = 0 )
{
return static_cast < qint64 > ( ts . tv_sec * 1000 + ts . tv_nsec / 1000000 ) ;
}
2019-01-17 03:23:22 -06:00
# else
2018-12-18 15:08:57 -06:00
return 0 ;
2019-01-17 03:23:22 -06:00
# endif
2018-12-18 15:08:57 -06:00
}