ecere: Initial Emscripten support
[sdk] / ecere / src / net / Socket.ec
1 #define _Noreturn
2
3 namespace net;
4
5 #include <stdarg.h>
6
7 #if defined(__WIN32__)
8
9 #define WIN32_LEAN_AND_MEAN
10 #define String _String
11 #include <winsock.h>
12 #undef String
13 #define SOCKLEN_TYPE int
14
15 #elif defined(__unix__) || defined(__APPLE__)
16
17 default:
18 #define SOCKLEN_TYPE socklen_t
19 #define set _set
20 #define uint _uint
21 #include <sys/time.h>
22 #include <unistd.h>
23
24 #include <netinet/in.h>
25 #include <netdb.h>
26 #include <sys/socket.h>
27 #include <sys/wait.h>
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <arpa/inet.h>
31 #undef set
32 #undef uint
33 private:
34
35 typedef int SOCKET;
36 typedef struct hostent HOSTENT;
37 typedef struct sockaddr SOCKADDR;
38 typedef struct sockaddr_in SOCKADDR_IN;
39 typedef struct in_addr IN_ADDR;
40 #define closesocket(s) close(s)
41
42 #endif
43
44 import "network"
45
46 #define GETLEDWORD(b) (uint32)(((b)[3] << 24) | ((b)[2] << 16) | ((b)[1] << 8) | (b)[0])
47
48 #define PUTLEDWORD(b, d) \
49    (b)[3] = (byte)(((d) >> 24) & 0xFF); \
50    (b)[2] = (byte)(((d) >> 16) & 0xFF); \
51    (b)[1] = (byte)(((d) >> 8)  & 0xFF); \
52    (b)[0] = (byte)( (d)        & 0xFF);
53
54 public enum SocketType { tcp, udp };
55 public enum DisconnectCode { remoteLost = 1, remoteClosed = 2, resolveFailed = 3, connectFailed = 4 };
56
57 public class Packet : struct
58 {
59 public:
60    uint size;
61 };
62
63 static class SocketConnectThread : Thread
64 {
65    Socket socket;
66
67    uint Main()
68    {
69       bool result = false;
70       HOSTENT * host = gethostbyname(socket.address);
71       if(host)
72       {
73          network.mutex.Wait();
74
75          if(!socket.destroyed)
76          {
77             socket.a.sin_addr = *((IN_ADDR *)host->h_addr);
78             network.mutex.Release();
79             if(socket.type == udp ||
80                !connect(socket.s,(SOCKADDR *)&socket.a,sizeof(socket.a)))
81             {
82                network.mutex.Wait();
83                strcpy(socket.inetAddress, inet_ntoa(socket.a.sin_addr));
84                socket.inetPort = ntohs(socket.a.sin_port);
85                network.mutex.Release();
86
87                if(socket.OnEstablishConnection((int)socket.s))
88                {
89                   network.mutex.Wait();
90                   result = true;
91                }
92                else
93                {
94                   network.mutex.Wait();
95                   socket.disconnectCode = connectFailed;
96                }
97             }
98             else
99             {
100                network.mutex.Wait();
101                socket.disconnectCode = connectFailed;
102             }
103          }
104       }
105       else
106          socket.disconnectCode = resolveFailed;
107
108    #ifdef DEBUG_SOCKETS
109       Log("[C] Signaling connect event (%X)\n", socket);
110    #endif
111       if(result && !socket.destroyed)
112          socket._connected = 1;
113       else if(socket._connected == -2)
114          socket._connected = -1;
115
116    #ifdef DEBUG_SOCKETS
117       Log("[C] Getting out of connect thread (%X)\n", socket);
118    #endif
119       network.connectEvent = true;
120       if(guiApp)
121          guiApp.SignalEvent();
122       network.mutex.Release();
123       return 0;
124    }
125
126 };
127
128 public class Socket
129 {
130 public:
131    property Service service
132    {
133       set
134       {
135          if(value)
136          {
137             SOCKET s;
138             SOCKADDR_IN a;
139             SOCKLEN_TYPE addrLen = sizeof(a);
140
141             value.accepted = true;
142             s = accept(value.s,(SOCKADDR *)&a, &addrLen);
143             if(s != -1)
144             {
145                int sendsize=65536;
146                int recvsize=65536;
147
148                value.sockets.Add(this);
149                incref this;
150
151                setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&sendsize, (int)sizeof(sendsize));
152                setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&recvsize, (int)sizeof(recvsize));
153
154                destroyed = false;
155                // TESTING THIS HERE
156                _connected = 1;
157                address = null;
158                this.a = a;
159                strcpy(inetAddress, inet_ntoa(this.a.sin_addr));
160                inetPort = ntohs(a.sin_port);
161                this.s = s;
162                service = value;
163                connectThread = null;
164                disconnectCode = (DisconnectCode)-1;
165                disconnected = false;
166
167                network.mutex.Wait();
168                FD_SET(s, &network.exceptSet);
169                FD_SET(s, &network.readSet);
170                if(s >= network.ns)
171                {
172                   network.ns = (int)(s+1);
173                   network.socketsSemaphore.Release();
174                }
175                network.mutex.Release();
176             }
177             else if(!_refCount)
178             {
179                delete this;
180             }
181          }
182       }
183       get { return this ? service : null; }
184    };
185
186    property const char * inetAddress { get { return (char *)inetAddress; } };
187    property int inetPort { get { return inetPort; } }
188    property Socket next { get { return next; } };
189    property bool connected { get { return _connected == 1 || _connected == -2; } };
190    property bool processAlone { get { return processAlone; } set { processAlone = value; } };
191
192    virtual void OnConnect(void);
193    virtual uint OnReceive(const byte * buffer, uint count)
194    {
195       if(count >= sizeof(class Packet))
196       {
197          Packet packet = (Packet)buffer;
198          uint size = GETLEDWORD((byte *)&packet.size);
199          if(count >= size)
200          {
201             byte * tempBuffer = null;
202             if(size)
203             {
204                if(recvBytes - size)
205                {
206                   tempBuffer = new byte[size];
207                   packet = (Packet)tempBuffer;
208                   memcpy(tempBuffer, buffer, size);
209                   memmove(recvBuffer, recvBuffer + size, recvBytes - size);
210                }
211                recvBytes -= size;
212             }
213             OnReceivePacket(packet);
214             delete tempBuffer;
215             return 0;
216          }
217       }
218       return 0;
219    }
220
221    virtual void OnDisconnect(int code);
222    virtual void OnReceivePacket(Packet packet);
223
224    bool Connect(const char * address, int port)
225    {
226       bool result = false;
227    #if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__)
228       SOCKET s = socket(AF_INET,SOCK_STREAM,0);
229    #ifdef DEBUG_SOCKETS
230       Log("\n[P] [NConnect]\n");
231    #endif
232       if(s != -1)
233       {
234          result = _Connect(s, address, port);
235       }
236    #endif
237       return result;
238    }
239
240    void Disconnect(DisconnectCode code)
241    {
242       if(this)
243       {
244          bool wasDisconnected = disconnected;
245    #if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__)
246          SOCKET s = this.s;
247
248    #ifdef DEBUG_SOCKETS
249          Logf("[P] [NDisconnect (%X, %x)]\n", this, this.s);
250    #endif
251          network.mutex.Wait();
252          disconnectCode = code;
253          destroyed = true;
254
255          if(!disconnected)
256          {
257             if(_connected == -2 && connectThread)
258             {
259                incref this;
260                network.mutex.Release();
261                connectThread.Wait();
262                delete connectThread;
263                network.mutex.Wait();
264                _refCount--;
265             }
266             disconnected = true;
267             if(!service)
268             {
269                if(_connected)
270                   ((_connected == -1 || _connected == -2) ? network.connectSockets : network.sockets).Remove(this);
271             }
272             else
273             {
274                service.sockets.Remove(this);
275                service = null;
276             }
277             _connected = 0;
278             network.mutex.Release();
279             OnDisconnect(disconnectCode);
280             network.mutex.Wait();
281          }
282
283          if(s == network.ns - 1)
284             Network_DetermineMaxSocket();
285
286          if(s != -1)
287          {
288             FD_CLR(s, &network.readSet);
289             FD_CLR(s, &network.writeSet);
290             FD_CLR(s, &network.exceptSet);
291          }
292          // Why wasn't this here? Don't want it here :) Hmm why don't we want it here? Service created socket not getting freed in DICOMTest...
293          // Trying >= 1 instead of > 1
294          // Free(code);
295          //if(_refCount > 1)
296          /*if(_refCount >= 1)
297             _refCount--;*/
298
299          shutdown(s, 2);
300
301          if(!wasDisconnected)
302             delete this;
303
304          network.mutex.Release();
305    #endif
306       }
307    }
308
309    // --- Transfer ---
310    bool Send(const void * buffer, int size)
311    {
312    #if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__)
313       if(this)
314       {
315          SOCKET s = this.s;
316          int count;
317          fd_set ws, es;
318
319          if(s != -1 && ((type == tcp && (count = SendData(buffer, size, 0))) ||
320             (type == udp && (count = (int)sendto(s, buffer, size,0, (SOCKADDR *)&a, sizeof(a))))))
321          {
322    #if defined(__WIN32__)
323             int error = WSAGetLastError();
324    #endif
325             FD_ZERO(&ws);
326             FD_ZERO(&es);
327             FD_SET(s, &ws);
328             FD_SET(s, &es);
329    #if defined(__WIN32__)
330             if(error)
331    #endif
332             {
333                //Print("~");
334             }
335
336             // This is what was making eCom jam...
337             // select(s+1, null, &ws, &es, null);
338             return true;
339          }
340       }
341    #endif
342       return false;
343    }
344
345    bool SendPacket(Packet packet)
346    {
347       if(this && packet)
348       {
349          bool result;
350          uint size = packet.size;
351          PUTLEDWORD((byte *)&packet.size, size);
352          result = Send(packet, size);
353          packet.size = size;
354          return result;
355       }
356       return false;
357    }
358
359    bool SendString(const char * string)
360    {
361       return Send(string, (int)strlen(string));
362    }
363
364    bool Sendf(const char * format, ...)
365    {
366       bool result;
367       va_list args;
368       char string[MAX_F_STRING];
369       va_start(args, format);
370       vsnprintf(string, sizeof(string), format, args);
371       string[sizeof(string)-1] = 0;
372       result = Send(string, (int)strlen(string));
373       va_end(args);
374       return result;
375    }
376
377    bool DatagramConnect(const char * sendAddress, int port)
378    {
379       SOCKET s = socket(AF_INET,SOCK_DGRAM,0);
380       if(s != -1)
381       {
382          _Connect(s, sendAddress, port);
383          type = udp;
384          return true;
385       }
386       return false;
387    }
388
389    bool DatagramHost(int port)
390    {
391       SOCKET s = socket(AF_INET,SOCK_DGRAM,0);
392       if(s != -1 && !_connected)
393       {
394          SOCKADDR_IN a;
395          bool val = true;
396          a.sin_family=AF_INET;
397          a.sin_port = htons((uint16)port);
398          a.sin_addr.s_addr=INADDR_ANY;
399          setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
400          if(!bind(s,(SOCKADDR *)&a, sizeof(a)))
401          {
402             network.mutex.Wait();
403             type = udp;
404             this.s = s;
405             network.sockets.Add(this);
406             incref this;
407
408             disconnectCode = (DisconnectCode)-1;
409             _connected = 1;
410
411             FD_CLR(s, &network.writeSet);
412             FD_SET(s, &network.readSet);
413             FD_SET(s, &network.exceptSet);
414             if(s >= network.ns)
415             {
416                network.ns = (int)(s+1);
417                network.socketsSemaphore.Release();
418             }
419             network.mutex.Release();
420             return true;
421          }
422          closesocket(s);
423       }
424       return false;
425    }
426
427    virtual int ReceiveData(byte * buffer, int count, uint flags)
428    {
429       return (int)recv(s, (char *)buffer, count, flags);
430    }
431    virtual int SendData(const byte * buffer, int count, uint flags)
432    {
433       return (int)send(s, (const char *)buffer, count, flags);
434    }
435    virtual bool OnEstablishConnection(int s);
436
437 private:
438    Socket()
439    {
440       Network_Initialize();
441       disconnected = true;
442    }
443
444    ~Socket()
445    {
446       _refCount = MAXINT;
447       Free(true);
448       _refCount = 0;
449    }
450
451 #ifndef ECERE_NONET
452    void Free(bool mustLock)
453    {
454       SOCKET s = this.s;
455
456       if(mustLock) network.mutex.Wait();
457
458       if(!service && _connected)
459       {
460          ((_connected == -1 || _connected == -2) ? network.connectSockets : network.sockets).Remove(this);
461          _connected = 0;
462       }
463
464       if(!disconnected)
465       {
466          disconnected = true;
467          network.mutex.Release();
468          incref this;
469          OnDisconnect(disconnectCode);
470          // if(_refCount > 1) _refCount--;
471          _refCount--;
472          network.mutex.Wait();
473       }
474
475       if(service)
476       {
477          service.sockets.Remove(this);
478          service = null;
479          _connected = 0;
480       }
481
482       if(s != -1) { closesocket(s); this.s = -1; }
483
484       delete address;
485       delete recvBuffer;
486
487       recvBufferSize = 0;
488       recvBytes = 0;
489
490       if(s != -1)
491       {
492          FD_CLR(s, &network.readSet);
493          FD_CLR(s, &network.writeSet);
494          FD_CLR(s, &network.exceptSet);
495       }
496
497       disconnectCode = 0;
498
499       // COMMENTED THIS OUT SINCE IT WAS INVALIDATING PROTECTION FOR HTTP FILE CONNECTION REUSE...
500       // WATCH FOR LEAKS IN OTHER PROJECTS?
501       //if(_refCount > 1) _refCount--;
502       if(mustLock) network.mutex.Release();
503    }
504
505    void _Disconnect(DisconnectCode code)
506    {
507       SOCKET s = this.s;
508       network.mutex.Wait();
509
510       disconnectCode = code;
511
512       Free(false);
513       delete this;
514
515       if(s == network.ns - 1)
516          Network_DetermineMaxSocket();
517       network.mutex.Release();
518    }
519
520    bool _Connect(SOCKET s, const char * address, int port)
521    {
522       bool result = false;
523       if(this)
524       {
525          SOCKADDR_IN a;
526          a.sin_family = AF_INET;
527          a.sin_port = htons((uint16)port);
528
529          network.mutex.Wait();
530
531          delete this.address;
532
533          // incref this;
534          destroyed = false;
535          this.address = new char[strlen(address)+1];
536          strcpy(this.address, address);
537          this.a = a;
538          this.s = s;
539          service = null;
540          disconnected = false;
541          disconnectCode = (DisconnectCode)-1;
542          connectThread = null;
543          _connected = -2;
544
545          FD_SET(s, &network.writeSet);
546          if(s >= network.ns && !processAlone)
547          {
548             network.ns = (int)(s+1);
549             network.socketsSemaphore.Release();
550          }
551          connectThread = SocketConnectThread { socket = this };
552
553          if(OnConnect == Socket::OnConnect)
554          {
555             result = true;
556
557             network.mutex.Release();
558             connectThread.Main();
559             network.mutex.Wait();
560
561             if(_connected == -1 || destroyed)
562             {
563                _connected = 0;
564
565                if(s == network.ns - 1)
566                   Network_DetermineMaxSocket();
567 #if 0
568                if(this.disconnectCode == resolveFailed)
569                   Logf("Error resolving address %s\n", this.address);
570 #endif
571                // Free();
572                this.s = -1;
573                result = false;
574             }
575             else if(_connected == 1)
576             {
577                FD_CLR(s, &network.writeSet);
578                FD_SET(s, &network.readSet);
579                FD_SET(s, &network.exceptSet);
580                network.sockets.Add(this);
581
582                incref this;
583                result = true;
584             }
585             else
586                this.s = -1;
587
588             delete connectThread;
589          }
590          else
591          {
592             network.connectSockets.Add(this);
593             incref this;
594             incref connectThread;
595             connectThread.Create();
596             result = true;
597          }
598          /*if(_refCount > 1)
599             delete this;*/
600       }
601       network.mutex.Release();
602       return result;
603    }
604
605    #define MAX_RECEIVE  65536
606
607    bool ProcessSocket(fd_set * rs, fd_set * ws, fd_set * es)
608    {
609       bool result = false;
610       SOCKET s;
611
612       incref this;
613       mutex.Wait();
614       // network.mutex.Wait();
615       s = this.s;
616       if(FD_ISSET(s, rs) || leftOver)
617       {
618          int count = 0;
619
620          result = true;
621          if((int)recvBufferSize - recvBytes < MAX_RECEIVE)
622          {
623             recvBuffer = renew recvBuffer byte[recvBufferSize + MAX_RECEIVE];
624             recvBufferSize += MAX_RECEIVE;
625          }
626
627          if(FD_ISSET(s, rs) && disconnectCode == (DisconnectCode)-1)
628          {
629             if(type == tcp /*|| _connected*/)
630                count = ReceiveData(recvBuffer + recvBytes, recvBufferSize - recvBytes, 0);
631             else
632             {
633                SOCKLEN_TYPE len = sizeof(a);
634                count = (int)recvfrom(s, (char *)recvBuffer + recvBytes,
635                   recvBufferSize - recvBytes, 0, (SOCKADDR *)&a, &len);
636                strcpy(inetAddress, inet_ntoa(this.a.sin_addr));
637                inetPort = ntohs((uint16)a.sin_port);
638             }
639             switch(count)
640             {
641                case 0:
642                   disconnectCode = remoteClosed;
643                   break;
644                case -1:
645                {
646                   /*int yo = errno;
647                   printf("Errno is %d", errno);*/
648                   disconnectCode = remoteLost;
649                   break;
650                }
651             }
652          }
653
654          if(count > 0 || (leftOver && !count))
655          {
656             uint flushCount;
657             leftOver = false;
658             recvBytes += count;
659             for(flushCount = 0; flushCount < recvBytes; )
660             {
661                uint recvCount = OnReceive(recvBuffer + flushCount, recvBytes - flushCount);
662                if(!recvCount)
663                {
664                   leftOver = true;
665                   if(!processAlone)
666                      network.leftOverBytes = true;
667                   break;
668                }
669                flushCount += recvCount;
670             }
671
672             if(flushCount < recvBytes)
673             {
674                if(flushCount)
675                {
676                   memmove(recvBuffer, recvBuffer + flushCount, recvBytes - flushCount);
677                   recvBytes -= flushCount;
678                }
679                else
680                {
681                   // If nothing was acknowledged, clear socket so that OnReceive doesn't keep getting called
682                   if(disconnectCode > -1)
683                      recvBytes = 0;
684                }
685             }
686             else
687                recvBytes = 0;
688          }
689       }
690       else if(FD_ISSET(s, es))
691       {
692          result = true;
693 #if 0
694          Logf("Remote Lost %s\n", (void *)inet_ntoa(a.sin_addr));
695 #endif
696          if(type != udp)
697             _Disconnect(remoteLost);
698       }
699
700       // Disconnect it here
701       if(!recvBytes && disconnectCode > -1 && _connected)
702       {
703          result = true;
704 #if 0
705          if(disconnectCode)
706             Logf("Disconnected (%d) %s\n", disconnectCode, (void *)inet_ntoa(a.sin_addr));
707 #endif
708          if(type != udp)
709             _Disconnect(disconnectCode);
710       }
711       // network.mutex.Release();
712       mutex.Release();
713
714       delete this;
715       return result;
716    }
717 #endif
718
719    public bool Process()
720    {
721       return ProcessTimeOut(0);
722    }
723
724    public bool ProcessTimeOut(Seconds timeOut)
725    {
726       bool gotEvent = false;
727       struct timeval tv = {0, 0};
728       struct timeval tvTO = {(uint)timeOut, (uint)((timeOut -(uint)timeOut)* 1000000)};
729       fd_set rs, ws, es;
730       int selectResult;
731
732       if(disconnectCode > 0 && !leftOver) return false;
733       FD_ZERO(&rs);
734       FD_ZERO(&ws);
735       FD_ZERO(&es);
736       FD_SET(s, &rs);
737       //FD_SET(s, &ws);
738       FD_SET(s, &es);
739
740       incref this;
741       selectResult = select((int)(s+1), &rs, &ws, &es, leftOver ? &tv : (timeOut ? &tvTO : null));
742       mutex.Wait();
743       if(s != -1 && _refCount && (leftOver || selectResult))
744       {
745          gotEvent |= ProcessSocket(&rs, &ws, &es);
746       }
747       mutex.Release();
748       delete this;
749       return gotEvent;
750    }
751
752    Service service;
753    bool leftOver;
754
755    char inetAddress[20];
756    int inetPort;
757
758    Socket prev, next;
759
760    SOCKET s;
761    char * address;
762    Thread connectThread;
763    DisconnectCode disconnectCode;
764    bool destroyed;
765    int _connected;         // -2: Initial value when calling Connect(), -1: Disconnected or otherwise destroyed while connecting, 1: succesfully connected, 0: no longer in any connect/sockets list
766    bool disconnected;
767
768    // Receiving Buffer
769    byte * recvBuffer;
770    uint recvBytes;
771    uint recvBufferSize;
772    SocketType type;
773    bool processAlone;
774    SOCKADDR_IN a;
775    Mutex mutex { };
776 };