From 152e160f7616e5b2ff36805a930b7c06346595aa Mon Sep 17 00:00:00 2001 From: Tobias Taschner Date: Tue, 6 Nov 2018 14:44:41 +0000 Subject: [PATCH] Initial NSURLSession implementation First incomplete implementation based on NSURLSession --- configure | 37 +++++ configure.in | 7 + include/wx/osx/webrequest_urlsession.h | 86 +++++++++- src/osx/webrequest_urlsession.mm | 218 ++++++++++++++++++++++++- 4 files changed, 335 insertions(+), 13 deletions(-) diff --git a/configure b/configure index f0b142da99..212a600a57 100755 --- a/configure +++ b/configure @@ -1139,6 +1139,7 @@ enable_ole enable_dataobj enable_webrequest enable_webrequestcurl +enable_webrequesturlsession enable_ipc enable_baseevtloop enable_epollloop @@ -2083,6 +2084,7 @@ Optional Features: --enable-dataobj use data object classes --enable-webrequest use wxWebRequest --enable-webrequest-curl use libcurl with wxWebRequest + --enable-webrequest-urlsession use NSURLSession with wxWebRequest --enable-ipc use interprocess communication (wxSocket etc.) --enable-baseevtloop use event loop in console programs too --enable-epollloop use wxEpollDispatcher class (Linux only) @@ -6580,7 +6582,37 @@ fi eval "$wx_cv_use_webrequestcurl" +if test "$USE_DARWIN" = 1; then + enablestring= + defaultval=$wxUSE_ALL_FEATURES + if test -z "$defaultval"; then + if test x"$enablestring" = xdisable; then + defaultval=yes + else + defaultval=no + fi + fi + + # Check whether --enable-webrequesturlsession was given. +if test "${enable_webrequesturlsession+set}" = set; then : + enableval=$enable_webrequesturlsession; + if test "$enableval" = yes; then + wx_cv_use_webrequesturlsession='wxUSE_WEBREQUEST_URLSESSION=yes' + else + wx_cv_use_webrequesturlsession='wxUSE_WEBREQUEST_URLSESSION=no' + fi + +else + + wx_cv_use_webrequesturlsession='wxUSE_WEBREQUEST_URLSESSION=${'DEFAULT_wxUSE_WEBREQUEST_URLSESSION":-$defaultval}" + +fi + + + eval "$wx_cv_use_webrequesturlsession" + +fi enablestring= defaultval=$wxUSE_ALL_FEATURES @@ -36714,6 +36746,11 @@ if test "$wxUSE_WEBREQUEST" = "yes"; then $as_echo "#define wxUSE_WEBREQUEST 1" >>confdefs.h + if test "$wxUSE_WEBREQUEST_URLSESSION" = "yes"; then + $as_echo "#define wxUSE_WEBREQUEST_URLSESSION 1" >>confdefs.h + + fi + if test "$wxUSE_MSW" = 1; then $as_echo "#define wxUSE_WEBREQUEST_WINHTTP 1" >>confdefs.h diff --git a/configure.in b/configure.in index 46c9067c47..76ce729155 100644 --- a/configure.in +++ b/configure.in @@ -700,6 +700,9 @@ WX_ARG_FEATURE(ole, [ --enable-ole use OLE classes (Win32 WX_ARG_FEATURE(dataobj, [ --enable-dataobj use data object classes], wxUSE_DATAOBJ) WX_ARG_FEATURE(webrequest, [ --enable-webrequest use wxWebRequest], wxUSE_WEBREQUEST) WX_ARG_FEATURE(webrequestcurl, [ --enable-webrequest-curl use libcurl with wxWebRequest], wxUSE_WEBREQUEST_LIBCURL) +if test "$USE_DARWIN" = 1; then +WX_ARG_FEATURE(webrequesturlsession, [ --enable-webrequest-urlsession use NSURLSession with wxWebRequest], wxUSE_WEBREQUEST_URLSESSION) +fi dnl USE_DARWIN WX_ARG_FEATURE(ipc, [ --enable-ipc use interprocess communication (wxSocket etc.)], wxUSE_IPC) @@ -6369,6 +6372,10 @@ fi if test "$wxUSE_WEBREQUEST" = "yes"; then AC_DEFINE(wxUSE_WEBREQUEST) + if test "$wxUSE_WEBREQUEST_URLSESSION" = "yes"; then + AC_DEFINE(wxUSE_WEBREQUEST_URLSESSION) + fi + if test "$wxUSE_MSW" = 1; then dnl TODO: Check for the required headers/libraries under Windows AC_DEFINE(wxUSE_WEBREQUEST_WINHTTP) diff --git a/include/wx/osx/webrequest_urlsession.h b/include/wx/osx/webrequest_urlsession.h index 4997ac7305..3e4646907e 100644 --- a/include/wx/osx/webrequest_urlsession.h +++ b/include/wx/osx/webrequest_urlsession.h @@ -13,28 +13,98 @@ #if wxUSE_WEBREQUEST_URLSESSION DECLARE_WXCOCOA_OBJC_CLASS(NSURLSession); -DECLARE_WXCOCOA_OBJC_CLASS(NSURLTask); +DECLARE_WXCOCOA_OBJC_CLASS(NSURLSessionTask); +DECLARE_WXCOCOA_OBJC_CLASS(wxWebSessionDelegte); + +class wxWebSessionURLSession; +class wxWebResponseURLSession; + +class WXDLLIMPEXP_NET wxWebResponseURLSession: public wxWebResponse +{ +public: + wxWebResponseURLSession(wxWebRequest& request, WX_NSURLSessionTask task); + + ~wxWebResponseURLSession(); + + wxInt64 GetContentLength() const wxOVERRIDE; + + wxString GetURL() const wxOVERRIDE; + + wxString GetHeader(const wxString& name) const wxOVERRIDE; + + int GetStatus() const wxOVERRIDE; + + wxString GetStatusText() const wxOVERRIDE; + + wxString GetSuggestedFileName() const wxOVERRIDE; + + void HandleData(WX_NSData data); + +private: + WX_NSURLSessionTask m_task; +}; + +class WXDLLIMPEXP_NET wxWebRequestURLSession: public wxWebRequest +{ +public: + wxWebRequestURLSession(wxWebSessionURLSession& session, const wxString& url, int id); + + ~wxWebRequestURLSession(); + + void Start() wxOVERRIDE; + + void Cancel() wxOVERRIDE; + + wxWebResponse* GetResponse() const wxOVERRIDE + { return m_response.get(); } + + wxWebAuthChallenge* GetAuthChallenge() const wxOVERRIDE; + + wxFileOffset GetBytesSent() const wxOVERRIDE; + + wxFileOffset GetBytesExpectedToSend() const wxOVERRIDE; + + wxFileOffset GetBytesReceived() const wxOVERRIDE; + + wxFileOffset GetBytesExpectedToReceive() const wxOVERRIDE; + + void HandleCompletion(); + +private: + wxString m_url; + WX_NSURLSessionTask m_task; + wxScopedPtr m_response; + + wxDECLARE_NO_COPY_CLASS(wxWebRequestURLSession); +}; class WXDLLIMPEXP_NET wxWebSessionURLSession: public wxWebSession { public: - wxWebSessionURLSession(); + wxWebSessionURLSession(); - ~wxWebSessionURLSession(); + ~wxWebSessionURLSession(); - wxWebRequest* CreateRequest(const wxString& url, int id = wxID_ANY) wxOVERRIDE; + wxWebRequest* CreateRequest(const wxString& url, int id = wxID_ANY) wxOVERRIDE; + + wxVersionInfo GetLibraryVersionInfo() wxOVERRIDE; + + WX_NSURLSession GetSession() { return m_session; } + + WX_wxWebSessionDelegte GetDelegate() { return m_delegate; } private: - WX_NSURLSession m_session; + WX_NSURLSession m_session; + WX_wxWebSessionDelegte m_delegate; - wxDECLARE_NO_COPY_CLASS(wxWebSessionURLSession); + wxDECLARE_NO_COPY_CLASS(wxWebSessionURLSession); }; class WXDLLIMPEXP_NET wxWebSessionFactoryURLSession: public wxWebSessionFactory { public: - wxWebSession* Create() wxOVERRIDE - { return new wxWebSessionURLSession(); } + wxWebSession* Create() wxOVERRIDE + { return new wxWebSessionURLSession(); } }; #endif // wxUSE_WEBREQUEST_URLSESSION diff --git a/src/osx/webrequest_urlsession.mm b/src/osx/webrequest_urlsession.mm index 7b241e8034..2ef93d5957 100644 --- a/src/osx/webrequest_urlsession.mm +++ b/src/osx/webrequest_urlsession.mm @@ -21,22 +21,230 @@ #import #include "wx/osx/webrequest_urlsession.h" +#include "wx/osx/private.h" + +#ifndef WX_PRECOMP + #include "wx/log.h" + #include "wx/utils.h" +#endif + +@interface wxWebSessionDelegte : NSObject +{ + wxWebSessionURLSession* m_session; + NSMapTable* m_requests; +} + +@end + +@implementation wxWebSessionDelegte + +- initWithSession:(wxWebSessionURLSession*)session +{ + m_session = session; + m_requests = [[NSMapTable weakToStrongObjectsMapTable] retain]; + return self; +} + +- (void)dealloc +{ + [m_requests release]; + [super dealloc]; +} + +- (void)registerRequest:(wxWebRequestURLSession*)request task:(NSURLSessionTask*)task +{ + [m_requests setObject:[NSValue valueWithPointer:request] forKey:task]; +} + +- (wxWebRequestURLSession*)requestForTask:(NSURLSessionTask*)task +{ + wxWebRequestURLSession* request = NULL; + NSValue* val = [m_requests objectForKey:task]; + if (val) + request = static_cast(val.pointerValue); + + return request; +} + +- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data +{ + wxWebRequestURLSession* request = [self requestForTask:dataTask]; + if (request) + static_cast(request->GetResponse())->HandleData(data); +} + +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error +{ + wxWebRequestURLSession* request = [self requestForTask:task]; + if (error) + request->SetState(wxWebRequest::State_Failed, wxCFStringRef(error.localizedDescription).AsString()); + else + request->HandleCompletion(); + + // After the task is completed it no longer needs to be mapped + [m_requests removeObjectForKey:task]; +} + +@end + +// +// wxWebRequestURLSession +// +wxWebRequestURLSession::wxWebRequestURLSession(wxWebSessionURLSession& session, const wxString& url, int id): + wxWebRequest(session, id), + m_url(url) +{ + +} + +wxWebRequestURLSession::~wxWebRequestURLSession() +{ + [m_task release]; +} + +void wxWebRequestURLSession::Start() +{ + wxWebSessionURLSession& session = static_cast(GetSession()); + + m_task = [[session.GetSession() dataTaskWithURL: + [NSURL URLWithString:wxCFStringRef(m_url).AsNSString()]] retain]; + + // The session delegate needs to know which task is wrapped in which request + [session.GetDelegate() registerRequest:this task:m_task]; + + m_response.reset(new wxWebResponseURLSession(*this, m_task)); + m_response->Init(); + + SetState(State_Active); + [m_task resume]; +} + +void wxWebRequestURLSession::Cancel() +{ + [m_task cancel]; +} + +void wxWebRequestURLSession::HandleCompletion() +{ + if ( CheckServerStatus() ) + SetState(State_Completed); +} + +wxWebAuthChallenge* wxWebRequestURLSession::GetAuthChallenge() const +{ + wxFAIL_MSG("not implemented"); + return NULL; +} + +wxFileOffset wxWebRequestURLSession::GetBytesSent() const +{ + return m_task.countOfBytesSent; +} + +wxFileOffset wxWebRequestURLSession::GetBytesExpectedToSend() const +{ + return m_task.countOfBytesExpectedToSend; +} + +wxFileOffset wxWebRequestURLSession::GetBytesReceived() const +{ + return m_task.countOfBytesReceived; +} + +wxFileOffset wxWebRequestURLSession::GetBytesExpectedToReceive() const +{ + return m_task.countOfBytesExpectedToReceive; +} + +// +// wxWebResponseURLSession +// + +wxWebResponseURLSession::wxWebResponseURLSession(wxWebRequest& request, WX_NSURLSessionTask task): + wxWebResponse(request) +{ + m_task = [task retain]; +} + +wxWebResponseURLSession::~wxWebResponseURLSession() +{ + [m_task release]; +} + +void wxWebResponseURLSession::HandleData(WX_NSData data) +{ + [data enumerateByteRangesUsingBlock:^(const void * _Nonnull bytes, NSRange byteRange, BOOL * _Nonnull stop) { + void* buf = GetDataBuffer(byteRange.length); + std::memcpy(buf, bytes, byteRange.length); + ReportDataReceived(byteRange.length); + }]; +} + +wxInt64 wxWebResponseURLSession::GetContentLength() const +{ + return m_task.response.expectedContentLength; +} + +wxString wxWebResponseURLSession::GetURL() const +{ + return wxCFStringRef(m_task.response.URL.absoluteString).AsString(); +} + +wxString wxWebResponseURLSession::GetHeader(const wxString& name) const +{ + NSHTTPURLResponse* httpResp = (NSHTTPURLResponse*) m_task.response; + NSString* value = [httpResp.allHeaderFields objectForKey:wxCFStringRef(name).AsNSString()]; + if (value) + return wxCFStringRef(value).AsString(); + else + return wxString(); +} + +int wxWebResponseURLSession::GetStatus() const +{ + NSHTTPURLResponse* httpResp = (NSHTTPURLResponse*) m_task.response; + return httpResp.statusCode; +} + +wxString wxWebResponseURLSession::GetStatusText() const +{ + return wxCFStringRef([NSHTTPURLResponse localizedStringForStatusCode:GetStatus()]).AsString(); +} + +wxString wxWebResponseURLSession::GetSuggestedFileName() const +{ + return wxCFStringRef(m_task.response.suggestedFilename).AsString(); +} + +// +// wxWebSessionURLSession +// wxWebSessionURLSession::wxWebSessionURLSession() { - m_session = [NSURLSession sessionWithConfiguration: - [NSURLSessionConfiguration defaultSessionConfiguration]]; + m_delegate = [[wxWebSessionDelegte alloc] initWithSession:this]; + + m_session = [[NSURLSession sessionWithConfiguration: + [NSURLSessionConfiguration defaultSessionConfiguration] + delegate:m_delegate delegateQueue:nil] retain]; } wxWebSessionURLSession::~wxWebSessionURLSession() { - [m_session release]; + [m_session release]; + [m_delegate release]; } wxWebRequest* wxWebSessionURLSession::CreateRequest(const wxString& url, int id) { - wxFAIL_MSG("not implemented"); - return NULL; + return new wxWebRequestURLSession(*this, url, id); +} + +wxVersionInfo wxWebSessionURLSession::GetLibraryVersionInfo() +{ + int verMaj, verMin, verMicro; + wxGetOsVersion(&verMaj, &verMin, &verMicro); + return wxVersionInfo("URLSession", verMaj, verMin, verMicro); } #endif // wxUSE_WEBREQUEST_URLSESSION