remote/network/RemoteRequestHandler.cpp (198 lines of code) (raw):

#include "RemoteRequestHandler.h" #include "../RpcExecutor.h" #include "../callback/RemoteAuthCallback.h" #include "../callback/RemoteCallback.h" #include "RemoteRequest.h" #include "RemoteResourceRequestHandler.h" #include "../router/MessageRoutersManager.h" #include "../browser/RemoteFrame.h" #include "../browser/RemoteBrowser.h" #include "../ServerHandlerContext.h" #include "include/cef_ssl_info.h" std::string err2str(cef_errorcode_t errorcode); namespace { std::string tstatus2str(cef_termination_status_t status); const bool doTrace = getBoolEnv("CEF_SERVER_TRACE_RemoteRequestHandler"); } RemoteRequestHandler::RemoteRequestHandler(std::shared_ptr<ServerHandlerContext> ctx, std::shared_ptr<MessageRoutersManager> routersManager) : myCtx(ctx), myRoutersManager(routersManager) { TRACE(); } RemoteRequestHandler::~RemoteRequestHandler() { TRACE(); // simple protection for leaking via callbacks for (auto c: myCallbacks) RemoteCallback::dispose(c); for (auto c: myAuthCallbacks) RemoteAuthCallback::dispose(c); } /// /// Called on the UI thread before browser navigation. Return true to cancel /// the navigation or false to allow the navigation to proceed. The |request| /// object cannot be modified in this callback. /// CefLoadHandler::OnLoadingStateChange will be called twice in all cases. /// If the navigation is allowed CefLoadHandler::OnLoadStart and /// CefLoadHandler::OnLoadEnd will be called. If the navigation is canceled /// CefLoadHandler::OnLoadError will be called with an |errorCode| value of /// ERR_ABORTED. The |user_gesture| value will be true if the browser /// navigated via explicit user gesture (e.g. clicking a link) or false if it /// navigated automatically (e.g. via the DomContentLoaded event). /// /*--cef()--*/ bool RemoteRequestHandler::OnBeforeBrowse(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, bool user_gesture, bool is_redirect ) { TRACE(); FIND_BID_OR_RETURN_VAL(false); // Forward request to ClientHandler to make the message_router_ happy. myRoutersManager->OnBeforeBrowse(browser, frame); RemoteRequest::Holder req(request); RemoteFrame::Holder frm(frame); return myCtx->javaService()->exec<bool>([&](JavaService s){ return s->RequestHandler_OnBeforeBrowse(bid, frm.serverId(), req.serverId(), user_gesture, is_redirect); }, false); } bool RemoteRequestHandler::OnOpenURLFromTab(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& target_url, WindowOpenDisposition target_disposition, bool user_gesture ) { TRACE(); FIND_BID_OR_RETURN_VAL(false); RemoteFrame::Holder frm(frame); return myCtx->javaService()->exec<bool>([&](JavaService s){ return s->RequestHandler_OnOpenURLFromTab(bid, frm.serverId(), target_url.ToString(), user_gesture); }, false); } /// /// Called on the browser process IO thread before a resource request is /// initiated. The |browser| and |frame| values represent the source of the /// request. |request| represents the request contents and cannot be modified /// in this callback. |is_navigation| will be true if the resource request is /// a navigation. |is_download| will be true if the resource request is a /// download. |request_initiator| is the origin (scheme + domain) of the page /// that initiated the request. Set |disable_default_handling| to true to /// disable default handling of the request, in which case it will need to be /// handled via CefResourceRequestHandler::GetResourceHandler or it will be /// canceled. To allow the resource load to proceed with default handling /// return NULL. To specify a handler for the resource return a /// CefResourceRequestHandler object. If this callback returns NULL the same /// method will be called on the associated CefRequestContextHandler, if any. /// CefRefPtr<CefResourceRequestHandler> RemoteRequestHandler::GetResourceRequestHandler( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, bool is_navigation, bool is_download, const CefString& request_initiator, bool& disable_default_handling ) { // Called on the browser process IO thread before a resource request is initiated. TRACE(); FIND_BID_OR_RETURN_VAL(nullptr); LogNdc ndc(__FILE_NAME__, __FUNCTION__, "ChromeIO"); RemoteRequest::Holder req(request); RemoteFrame::Holder frm(frame); thrift_codegen::RObject peer; myCtx->javaServiceIO()->exec([&](JavaService s){ s->RequestHandler_GetResourceRequestHandler( peer, bid, frm.serverId(), req.serverId(), is_navigation, is_download, request_initiator.ToString()); }); disable_default_handling = peer.__isset.flags ? peer.flags != 0 : false; return !peer.isNull ? new RemoteResourceRequestHandler(bid, myCtx, peer) : nullptr; } /// /// Called on the IO thread when the browser needs credentials from the user. /// |origin_url| is the origin making this authentication request. |isProxy| /// indicates whether the host is a proxy server. |host| contains the hostname /// and |port| contains the port number. |realm| is the realm of the challenge /// and may be empty. |scheme| is the authentication scheme used, such as /// "basic" or "digest", and will be empty if the source of the request is an /// FTP server. Return true to continue the request and call /// CefAuthCallback::Continue() either in this method or at a later time when /// the authentication information is available. Return false to cancel the /// request immediately. /// /*--cef(optional_param=realm,optional_param=scheme)--*/ bool RemoteRequestHandler::GetAuthCredentials(CefRefPtr<CefBrowser> browser, const CefString& origin_url, bool isProxy, const CefString& host, int port, const CefString& realm, const CefString& scheme, CefRefPtr<CefAuthCallback> callback ) { TRACE(); FIND_BID_OR_RETURN_VAL(false); thrift_codegen::RObject rc = RemoteAuthCallback::wrapDelegate(callback)->serverId(); const bool handled = myCtx->javaServiceIO()->exec<bool>([&](JavaService s){ return s->RequestHandler_GetAuthCredentials(bid, origin_url.ToString(), isProxy, host.ToString(), port, realm.ToString(), scheme.ToString(), rc); }, false); if (!handled) RemoteAuthCallback::dispose(rc.objId); else myAuthCallbacks.insert(rc.objId); // Callback will be disposed with RemoteRequestHandler (just for insurance) return handled; } void writeSSLData(std::string & out, CefRefPtr<CefSSLInfo> sslInfo); /// /// Called on the UI thread to handle requests for URLs with an invalid /// SSL certificate. Return true and call CefCallback methods either in this /// method or at a later time to continue or cancel the request. Return false /// to cancel the request immediately. If /// cef_settings_t.ignore_certificate_errors is set all invalid certificates /// will be accepted without calling this method. /// /*--cef()--*/ bool RemoteRequestHandler::OnCertificateError(CefRefPtr<CefBrowser> browser, cef_errorcode_t cert_error, const CefString& request_url, CefRefPtr<CefSSLInfo> ssl_info, CefRefPtr<CefCallback> callback ) { TRACE(); FIND_BID_OR_RETURN_VAL(false); std::shared_ptr<RemoteCallback> rc = RemoteCallback::wrapDelegate(callback); std::string buf; writeSSLData(buf, ssl_info); if (buf.capacity() > 1024*128) Log::warn("Large SSL certificate data: %d bytes. Consider to use shared memory for IPC transport.", buf.capacity()); const bool handled = myCtx->javaService()->exec<bool>([&](JavaService s){ return s->RequestHandler_OnCertificateError(bid, err2str(cert_error), request_url, buf, rc->serverId()); }, false); if (!handled) RemoteCallback::dispose(rc->getId()); else myCallbacks.insert(rc->getId()); // Callback will be disposed with RemoteRequestHandler (just for insurance) return handled; } size_t getAligned(size_t bytesCount) { const int diff = bytesCount % 4; return diff == 0 ? bytesCount : bytesCount + 4 - diff; } void writeSSLData(std::string & out, CefRefPtr<CefSSLInfo> sslInfo) { CefRefPtr<CefX509Certificate> cert = sslInfo->GetX509Certificate(); // 1. Collect CefX509Certificate::IssuerChainBinaryList der_chain; cert->GetDEREncodedIssuerChain(der_chain); der_chain.insert(der_chain.begin(), cert->GetDEREncoded()); // 2. Calculate size size_t totalBinaryDataSize = 0; for (const auto& it: der_chain) { const size_t size = it->GetSize(); totalBinaryDataSize += getAligned(size); } // 3. Write const size_t reserved = totalBinaryDataSize + 4/*status mask*/ + 4/*items count*/ + 4*der_chain.size(); out.resize(reserved); int32_t* p = (int32_t*)out.data(); *(p++) = sslInfo->GetCertStatus(); *(p++) = (int32_t)der_chain.size(); //Log::trace("writeSSLData: chain size = %d, status = %d.", der_chain.size(), sslInfo->GetCertStatus()); if (der_chain.size() == 0) return; for (const auto& der_cert: der_chain) { const size_t bytesCount = der_cert->GetSize(); //Log::trace("writeSSLData: write chunk of size %d bytes, pos=%d.", bytesCount, ((char*)p) - out.data()); *(p++) = (int32_t)bytesCount; if (bytesCount == 0) continue; der_cert->GetData(p, bytesCount, 0); p += getAligned(bytesCount)/4; } } void RemoteRequestHandler::OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser, TerminationStatus status, int error_code, const CefString& error_string) { TRACE(); FIND_BID_OR_RETURN(); // Forward request to ClientHandler to make the message_router_ happy. myRoutersManager->OnRenderProcessTerminated(browser); myCtx->javaService()->exec([&](JavaService s){ s->RequestHandler_OnRenderProcessTerminated(bid, tstatus2str(status), error_code, error_string); }); } namespace { std::pair<cef_errorcode_t, std::string> errorcodes [] = { #define NET_ERROR(label, value) {ERR_##label,"ERR_"#label}, #include "include/base/internal/cef_net_error_list.h" #undef NET_ERROR {ERR_NONE, "ERR_NONE"} }; std::pair<cef_termination_status_t, std::string> terminationStatuses [] = { {TS_ABNORMAL_TERMINATION, "TS_ABNORMAL_TERMINATION"}, {TS_PROCESS_WAS_KILLED, "TS_PROCESS_WAS_KILLED"}, {TS_PROCESS_CRASHED, "TS_PROCESS_CRASHED"}, {TS_PROCESS_OOM, "TS_PROCESS_OOM"}, {TS_LAUNCH_FAILED, "TS_LAUNCH_FAILED"}, {TS_INTEGRITY_FAILURE, "TS_INTEGRITY_FAILURE"}, {TS_NUM_VALUES, "TS_NUM_VALUES"} }; std::string tstatus2str(cef_termination_status_t status) { for (auto p: terminationStatuses) { if (p.first == status) return p.second; } return string_format("unknown_termination_status_%d", status); } } std::string err2str(cef_errorcode_t errorcode) { for (auto p: errorcodes) { if (p.first == errorcode) return p.second; } Log::error("Can't find cef_errorcode_t: %d", errorcode); return string_format("unknown_errorcode_%d", errorcode); } cef_errorcode_t str2err(std::string err) { for (auto p: errorcodes) { if (p.second.compare(err) == 0) return p.first; } Log::error("Can't find cef_errorcode_t: %s", err.c_str()); return ERR_NONE; }