summaryrefslogtreecommitdiff
path: root/pjlib/src/pj/sock_uwp.h
diff options
context:
space:
mode:
Diffstat (limited to 'pjlib/src/pj/sock_uwp.h')
-rw-r--r--pjlib/src/pj/sock_uwp.h311
1 files changed, 311 insertions, 0 deletions
diff --git a/pjlib/src/pj/sock_uwp.h b/pjlib/src/pj/sock_uwp.h
new file mode 100644
index 00000000..6647f3ac
--- /dev/null
+++ b/pjlib/src/pj/sock_uwp.h
@@ -0,0 +1,311 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2016 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#pragma once
+
+
+#include <pj/assert.h>
+#include <pj/sock.h>
+#include <pj/string.h>
+#include <pj/unicode.h>
+
+
+enum {
+ READ_TIMEOUT = 60 * 1000,
+ WRITE_TIMEOUT = 60 * 1000,
+ SEND_BUFFER_SIZE = 128 * 1024,
+};
+
+enum PjUwpSocketType {
+ SOCKTYPE_UNKNOWN, SOCKTYPE_LISTENER,
+ SOCKTYPE_STREAM, SOCKTYPE_DATAGRAM
+};
+
+enum PjUwpSocketState {
+ SOCKSTATE_NULL, SOCKSTATE_INITIALIZED, SOCKSTATE_CONNECTING,
+ SOCKSTATE_CONNECTED, SOCKSTATE_DISCONNECTED, SOCKSTATE_ERROR
+};
+
+ref class PjUwpSocketDatagramRecvHelper;
+ref class PjUwpSocketListenerHelper;
+class PjUwpSocket;
+
+
+typedef struct PjUwpSocketCallback
+{
+ void (*on_read)(PjUwpSocket *s, int bytes_read);
+ void (*on_write)(PjUwpSocket *s, int bytes_sent);
+ void (*on_accept)(PjUwpSocket *s);
+ void (*on_connect)(PjUwpSocket *s, pj_status_t status);
+} PjUwpSocketCallback;
+
+
+/*
+ * UWP Socket Wrapper.
+ */
+class PjUwpSocket
+{
+public:
+ PjUwpSocket(int af_, int type_, int proto_);
+ virtual ~PjUwpSocket();
+ pj_status_t InitSocket(enum PjUwpSocketType sock_type_);
+ void DeinitSocket();
+
+ void* GetUserData() { return user_data; }
+ void SetNonBlocking(const PjUwpSocketCallback *cb_, void *user_data_)
+ {
+ is_blocking = PJ_FALSE;
+ cb=*cb_;
+ user_data = user_data_;
+ }
+
+ enum PjUwpSocketType GetType() { return sock_type; }
+ enum PjUwpSocketState GetState() { return sock_state; }
+
+ pj_sockaddr* GetLocalAddr() { return &local_addr; }
+ pj_sockaddr* GetRemoteAddr() { return &remote_addr; }
+
+
+ pj_status_t Bind(const pj_sockaddr_t *addr = NULL);
+ pj_status_t Send(const void *buf, pj_ssize_t *len);
+ pj_status_t SendTo(const void *buf, pj_ssize_t *len, const pj_sockaddr_t *to);
+ pj_status_t Recv(void *buf, pj_ssize_t *len);
+ pj_status_t RecvFrom(void *buf, pj_ssize_t *len, pj_sockaddr_t *from);
+ pj_status_t Connect(const pj_sockaddr_t *addr);
+ pj_status_t Listen();
+ pj_status_t Accept(PjUwpSocket **new_sock);
+
+ void (*on_read)(PjUwpSocket *s, int bytes_read);
+ void (*on_write)(PjUwpSocket *s, int bytes_sent);
+ void (*on_accept)(PjUwpSocket *s, pj_status_t status);
+ void (*on_connect)(PjUwpSocket *s, pj_status_t status);
+
+private:
+ PjUwpSocket* CreateAcceptSocket(Windows::Networking::Sockets::StreamSocket^ stream_sock_);
+ pj_status_t SendImp(const void *buf, pj_ssize_t *len);
+ int ConsumeReadBuffer(void *buf, int max_len);
+
+ int af;
+ int type;
+ int proto;
+ pj_sockaddr local_addr;
+ pj_sockaddr remote_addr;
+ pj_bool_t is_blocking;
+ pj_bool_t has_pending_bind;
+ pj_bool_t has_pending_send;
+ pj_bool_t has_pending_recv;
+ void *user_data;
+ PjUwpSocketCallback cb;
+
+ enum PjUwpSocketType sock_type;
+ enum PjUwpSocketState sock_state;
+ Windows::Networking::Sockets::DatagramSocket^ datagram_sock;
+ Windows::Networking::Sockets::StreamSocket^ stream_sock;
+ Windows::Networking::Sockets::StreamSocketListener^ listener_sock;
+
+ /* Helper objects */
+ PjUwpSocketDatagramRecvHelper^ dgram_recv_helper;
+ PjUwpSocketListenerHelper^ listener_helper;
+
+ Windows::Storage::Streams::DataReader^ socket_reader;
+ Windows::Storage::Streams::DataWriter^ socket_writer;
+ Windows::Storage::Streams::IBuffer^ send_buffer;
+
+ friend PjUwpSocketDatagramRecvHelper;
+ friend PjUwpSocketListenerHelper;
+};
+
+
+//////////////////////////////////
+// Misc
+
+
+inline pj_status_t wstr_addr_to_sockaddr(const wchar_t *waddr,
+ const wchar_t *wport,
+ pj_sockaddr_t *sockaddr)
+{
+#if 0
+ char tmp_str_buf[PJ_INET6_ADDRSTRLEN+1];
+ pj_assert(wcslen(waddr) < sizeof(tmp_str_buf));
+ pj_unicode_to_ansi(waddr, wcslen(waddr), tmp_str_buf, sizeof(tmp_str_buf));
+ pj_str_t remote_host;
+ pj_strset(&remote_host, tmp_str_buf, pj_ansi_strlen(tmp_str_buf));
+ pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &remote_host, (pj_sockaddr*)sockaddr);
+ pj_sockaddr_set_port((pj_sockaddr*)sockaddr, (pj_uint16_t)_wtoi(wport));
+
+ return PJ_SUCCESS;
+#endif
+ char tmp_str_buf[PJ_INET6_ADDRSTRLEN+1];
+ pj_assert(wcslen(waddr) < sizeof(tmp_str_buf));
+ pj_unicode_to_ansi(waddr, wcslen(waddr), tmp_str_buf, sizeof(tmp_str_buf));
+ pj_str_t remote_host;
+ pj_strset(&remote_host, tmp_str_buf, pj_ansi_strlen(tmp_str_buf));
+ pj_sockaddr *addr = (pj_sockaddr*)sockaddr;
+ pj_bool_t got_addr = PJ_FALSE;
+
+ if (pj_inet_pton(PJ_AF_INET, &remote_host, &addr->ipv4.sin_addr)
+ == PJ_SUCCESS)
+ {
+ addr->addr.sa_family = PJ_AF_INET;
+ got_addr = PJ_TRUE;
+ } else if (pj_inet_pton(PJ_AF_INET6, &remote_host, &addr->ipv6.sin6_addr)
+ == PJ_SUCCESS)
+ {
+ addr->addr.sa_family = PJ_AF_INET6;
+ got_addr = PJ_TRUE;
+ }
+ if (!got_addr)
+ return PJ_EINVAL;
+
+ pj_sockaddr_set_port(addr, (pj_uint16_t)_wtoi(wport));
+ return PJ_SUCCESS;
+}
+
+
+inline pj_status_t sockaddr_to_hostname_port(const pj_sockaddr_t *sockaddr,
+ Windows::Networking::HostName ^&hostname,
+ int *port)
+{
+ char tmp[PJ_INET6_ADDRSTRLEN];
+ wchar_t wtmp[PJ_INET6_ADDRSTRLEN];
+ pj_sockaddr_print(sockaddr, tmp, PJ_INET6_ADDRSTRLEN, 0);
+ pj_ansi_to_unicode(tmp, pj_ansi_strlen(tmp), wtmp,
+ PJ_INET6_ADDRSTRLEN);
+ hostname = ref new Windows::Networking::HostName(ref new Platform::String(wtmp));
+ *port = pj_sockaddr_get_port(sockaddr);
+
+ return PJ_SUCCESS;
+}
+
+
+/* Buffer helper */
+
+#include <Robuffer.h>
+#include <wrl/client.h>
+
+inline Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> GetBufferByteAccess(Windows::Storage::Streams::IBuffer^ buffer)
+{
+ auto pUnk = reinterpret_cast<IUnknown*>(buffer);
+
+ Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> comBuff;
+ pUnk->QueryInterface(__uuidof(Windows::Storage::Streams::IBufferByteAccess), (void**)comBuff.ReleaseAndGetAddressOf());
+
+ return comBuff;
+}
+
+
+inline void GetRawBufferFromIBuffer(Windows::Storage::Streams::IBuffer^ buffer, unsigned char** pbuffer)
+{
+ Platform::Object^ obj = buffer;
+ Microsoft::WRL::ComPtr<IInspectable> insp(reinterpret_cast<IInspectable*>(obj));
+ Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess;
+ insp.As(&bufferByteAccess);
+ bufferByteAccess->Buffer(pbuffer);
+}
+
+inline void CopyToIBuffer(unsigned char* buffSource, unsigned int copyByteCount, Windows::Storage::Streams::IBuffer^ buffer, unsigned int writeStartPos = 0)
+{
+ auto bufferLen = buffer->Capacity;
+ assert(copyByteCount <= bufferLen);
+
+ unsigned char* pBuffer;
+
+ GetRawBufferFromIBuffer(buffer, &pBuffer);
+
+ memcpy(pBuffer + writeStartPos, buffSource, copyByteCount);
+}
+
+inline void CopyFromIBuffer(unsigned char* buffDestination, unsigned int copyByteCount, Windows::Storage::Streams::IBuffer^ buffer, unsigned int readStartPos = 0)
+{
+ assert(copyByteCount <= buffer->Capacity);
+
+ unsigned char* pBuffer;
+
+ GetRawBufferFromIBuffer(buffer, &pBuffer);
+
+ memcpy(buffDestination, pBuffer + readStartPos, copyByteCount);
+}
+
+
+/* PPL helper */
+
+#include <ppltasks.h>
+#include <agents.h>
+
+// Creates a task that completes after the specified delay, in ms.
+inline concurrency::task<void> complete_after(unsigned int timeout)
+{
+ // A task completion event that is set when a timer fires.
+ concurrency::task_completion_event<void> tce;
+
+ // Create a non-repeating timer.
+ auto fire_once = new concurrency::timer<int>(timeout, 0, nullptr, false);
+ // Create a call object that sets the completion event after the timer fires.
+ auto callback = new concurrency::call<int>([tce](int)
+ {
+ tce.set();
+ });
+
+ // Connect the timer to the callback and start the timer.
+ fire_once->link_target(callback);
+ fire_once->start();
+
+ // Create a task that completes after the completion event is set.
+ concurrency::task<void> event_set(tce);
+
+ // Create a continuation task that cleans up resources and
+ // and return that continuation task.
+ return event_set.then([callback, fire_once]()
+ {
+ delete callback;
+ delete fire_once;
+ });
+}
+
+// Cancels the provided task after the specifed delay, if the task
+// did not complete.
+template<typename T>
+inline concurrency::task<T> cancel_after_timeout(concurrency::task<T> t, concurrency::cancellation_token_source cts, unsigned int timeout)
+{
+ // Create a task that returns true after the specified task completes.
+ concurrency::task<bool> success_task = t.then([](T)
+ {
+ return true;
+ });
+ // Create a task that returns false after the specified timeout.
+ concurrency::task<bool> failure_task = complete_after(timeout).then([]
+ {
+ return false;
+ });
+
+ // Create a continuation task that cancels the overall task
+ // if the timeout task finishes first.
+ return (failure_task || success_task).then([t, cts](bool success)
+ {
+ if (!success)
+ {
+ // Set the cancellation token. The task that is passed as the
+ // t parameter should respond to the cancellation and stop
+ // as soon as it can.
+ cts.cancel();
+ }
+
+ // Return the original task.
+ return t;
+ });
+}