1 module hunt.io.channel.posix.AbstractStream;
2 
3 // dfmt off
4 version(Posix):
5 // dfmt on
6 
7 import hunt.collection.BufferUtils;
8 import hunt.collection.ByteBuffer;
9 import hunt.event.selector.Selector;
10 import hunt.Functions;
11 import hunt.io.channel.AbstractSocketChannel;
12 import hunt.io.channel.Common;
13 import hunt.io.IoError;
14 import hunt.logging.ConsoleLogger;
15 import hunt.system.Error;
16 
17 import std.format;
18 import std.socket;
19 
20 import core.atomic;
21 import core.stdc.errno;
22 import core.stdc.string;
23 import core.sys.posix.sys.socket : accept;
24 import core.sys.posix.unistd;
25 
26 enum string ResponseData = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\nConnection: Keep-Alive\r\nContent-Type: text/plain\r\nServer: Hunt/1.0\r\nDate: Wed, 17 Apr 2013 12:00:00 GMT\r\n\r\nHello, World!";
27 
28 /**
29 TCP Peer
30 */
31 abstract class AbstractStream : AbstractSocketChannel {
32     enum BufferSize = 4096;
33     private const(ubyte)[] _readBuffer;
34     private ByteBuffer writeBuffer;
35 
36     /**
37     * Warning: The received data is stored a inner buffer. For a data safe,
38     * you would make a copy of it.
39     */
40     protected DataReceivedHandler dataReceivedHandler;
41     protected SimpleEventHandler disconnectionHandler;
42     protected SimpleActionHandler dataWriteDoneHandler;
43 
44     protected AddressFamily _family;
45     protected ByteBuffer _bufferForRead;
46     protected WritingBufferQueue _writeQueue;
47     protected bool isWriteCancelling = false;
48 
49     this(Selector loop, AddressFamily family = AddressFamily.INET, size_t bufferSize = 4096 * 2) {
50         this._family = family;
51         _bufferForRead = BufferUtils.allocate(bufferSize);
52         _bufferForRead.limit(cast(int)bufferSize);
53         _readBuffer = cast(ubyte[])_bufferForRead.array();
54         _writeQueue = new WritingBufferQueue();
55         super(loop, ChannelType.TCP);
56         setFlag(ChannelFlag.Read, true);
57         setFlag(ChannelFlag.Write, true);
58         setFlag(ChannelFlag.ETMode, true);
59     }
60 
61     abstract bool isConnected() nothrow;
62     abstract protected void onDisconnected();
63 
64     /**
65      *
66      */
67     protected bool tryRead() {
68         bool isDone = true;
69         this.clearError();
70         ptrdiff_t len = read(this.handle, cast(void*) _readBuffer.ptr, _readBuffer.length);
71 
72         // ubyte[] rb = new ubyte[BufferSize];
73         // ptrdiff_t len = read(this.handle, cast(void*) rb.ptr, rb.length);
74         version (HUNT_IO_DEBUG) {
75             tracef("reading[fd=%d]: %d bytes", this.handle, len);
76             if (len <= 32)
77                 infof("fd: %d, %d bytes: %(%02X %)", this.handle, len, _readBuffer[0 .. len]);
78             else
79                 infof("fd: %d, 32/%d bytes: %(%02X %)", this.handle, len, _readBuffer[0 .. 32]);
80         }
81 
82         if (len > 0) {
83             if (dataReceivedHandler !is null) {
84                 _bufferForRead.limit(cast(int)len);
85                 _bufferForRead.position(0);
86                 dataReceivedHandler(_bufferForRead);
87 
88                 // ByteBuffer bb = BufferUtils.wrap(cast(byte[])rb[0..len]);
89                 // dataReceivedHandler(bb);
90             }
91 
92             // size_t nBytes = tryWrite(cast(ubyte[])ResponseData);
93 
94             // if(nBytes < ResponseData.length) {
95             //     warning("data lost");
96             // }
97 
98             // It's prossible that there are more data waitting for read in the read I/O space.
99             if (len == _readBuffer.length) {
100                 version (HUNT_IO_DEBUG) infof("Read buffer is full read %d bytes. Need to read again.", len);
101                 isDone = false;
102             }
103         } else if (len == Socket.ERROR) {
104             // https://stackoverflow.com/questions/14595269/errno-35-eagain-returned-on-recv-call
105             // FIXME: Needing refactor or cleanup -@Administrator at 2018-5-8 16:06:13
106             // check more error status
107             this._error = errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK;
108             if (_error) {
109                 this._errorMessage = getErrorMessage(errno);
110                 errorOccurred(ErrorCode.INTERRUPTED , "read buffer error");
111             } else {
112                 debug warningf("warning on read: fd=%d, errno=%d, message=%s", this.handle,
113                         errno, getErrorMessage(errno));
114             }
115 
116             if(errno == ECONNRESET) {
117                 // https://stackoverflow.com/questions/1434451/what-does-connection-reset-by-peer-mean
118                 onDisconnected();
119                 errorOccurred(ErrorCode.CONNECTIONEESET , "connection reset by peer");
120             }
121         }
122         else {
123             version (HUNT_DEBUG)
124                 infof("connection broken: %s, fd:%d", _remoteAddress.toString(), this.handle);
125             onDisconnected();
126         }
127 
128         return isDone;
129     }
130 
131     override protected void doClose() {
132         version (HUNT_IO_DEBUG) {
133             infof("peer socket closing: fd=%d", this.handle);
134         }
135         if(this.socket is null) {
136             import core.sys.posix.unistd;
137             core.sys.posix.unistd.close(this.handle);
138         } else {
139             this.socket.shutdown(SocketShutdown.BOTH);
140             this.socket.close();
141         }
142 
143         version (HUNT_IO_DEBUG) {
144             infof("peer socket closed: fd=%d", this.handle);
145         }
146     }
147 
148 
149     /**
150      * Try to write a block of data.
151      */
152     protected ptrdiff_t tryWrite(const ubyte[] data) {
153         clearError();
154         // const nBytes = this.socket.send(data);
155         version (HUNT_IO_DEBUG)
156             tracef("try to write: %d bytes, fd=%d", data.length, this.handle);
157         const nBytes = write(this.handle, data.ptr, data.length);
158         // version (HUNT_IO_DEBUG)
159             // tracef("actually written: %d / %d bytes, fd=%d", nBytes, data.length, this.handle);
160 
161         if (nBytes > 0) {
162             return nBytes;
163         }
164 
165         if (nBytes == Socket.ERROR) {
166             // FIXME: Needing refactor or cleanup -@Administrator at 2018-5-8 16:07:38
167             // check more error status
168             // EPIPE/Broken pipe:
169             // https://stackoverflow.com/questions/6824265/sigpipe-broken-pipe
170             // https://github.com/angrave/SystemProgramming/wiki/Networking%2C-Part-7%3A-Nonblocking-I-O%2C-select%28%29%2C-and-epoll
171 
172             if(errno == EAGAIN) {
173                 version (HUNT_IO_DEBUG) {
174                     warningf("warning on write: fd=%d, errno=%d, message=%s", this.handle,
175                         errno, getErrorMessage(errno));
176                 }
177             } else if(errno == EINTR || errno == EWOULDBLOCK) {
178                 // https://stackoverflow.com/questions/38964745/can-a-socket-become-writeable-after-an-ewouldblock-but-before-an-epoll-wait
179                 debug warningf("warning on write: fd=%d, errno=%d, message=%s", this.handle,
180                         errno, getErrorMessage(errno));
181                 // eventLoop.update(this);
182             } else {
183                 this._error = true;
184                 this._errorMessage = getErrorMessage(errno);
185                 if(errno == ECONNRESET) {
186                     // https://stackoverflow.com/questions/1434451/what-does-connection-reset-by-peer-mean
187                     onDisconnected();
188                     errorOccurred(ErrorCode.CONNECTIONEESET , "connection reset by peer");
189                 }
190             }
191         } else {
192             version (HUNT_DEBUG) {
193                 warningf("nBytes=%d, message: %s", nBytes, lastSocketError());
194                 assert(false, "Undefined behavior!");
195             } else {
196                 this._error = true;
197             }
198         }
199 
200         //if (this._error) {
201         //    this._errorMessage = getErrorMessage(errno);
202         //    string msg = format("Socket error on write: fd=%d, code: %d, message=%s",
203         //            this.handle, errno, this.errorMessage);
204         //    debug errorf(msg);
205         //    errorOccurred(msg);
206         //}
207 
208         return 0;
209     }
210 
211     private bool tryNextWrite(ByteBuffer buffer) {
212         const(ubyte)[] data = cast(const(ubyte)[])buffer.getRemaining();
213         version (HUNT_IO_DEBUG) {
214             tracef("writting from a buffer [fd=%d], %d bytes, buffer: %s",
215                 this.handle, data.length, buffer.toString());
216         }
217 
218         ptrdiff_t remaining = data.length;
219         if(data.length == 0)
220             return true;
221 
222         while(remaining > 0 && !_error && !isClosing() && !isWriteCancelling) {
223             ptrdiff_t nBytes = tryWrite(data);
224             version (HUNT_IO_DEBUG)
225             {
226                 tracef("write out once: fd=%d, %d / %d bytes, remaining: %d buffer: %s",
227                     this.handle, nBytes, data.length, remaining, buffer.toString());
228             }
229 
230             if (nBytes > 0) {
231                 remaining -= nBytes;
232                 data = data[nBytes .. $];
233             }
234         }
235 
236         version (HUNT_IO_DEBUG) {
237             if(remaining == 0) {
238                     tracef("A buffer is written out. fd=%d", this.handle);
239                 return true;
240             } else {
241                 warningf("Writing cancelled or an error ocurred. fd=%d", this.handle);
242                 return false;
243             }
244         } else {
245             return remaining == 0;
246         }
247     }
248 
249     void resetWriteStatus() {
250         _writeQueue.clear();
251         atomicStore(_isWritting, false);
252         isWriteCancelling = false;
253     }
254 
255     /**
256      * Should be thread-safe.
257      */
258     override void onWrite() {
259         version (HUNT_IO_DEBUG)
260         {
261             tracef("checking status, isWritting: %s, writeBuffer: %s",
262                 _isWritting, writeBuffer is null ? "null" : writeBuffer.toString());
263         }
264 
265         if(!_isWritting) {
266             version (HUNT_IO_DEBUG)
267             infof("No data needs to be written out. fd=%d", this.handle);
268             return;
269         }
270 
271         if(isClosing() && isWriteCancelling) {
272             version (HUNT_DEBUG) infof("Write cancelled or closed, fd=%d", this.handle);
273             resetWriteStatus();
274             return;
275         }
276 
277         // keep thread-safe here
278         if(!cas(&_isBusyWritting, false, true)) {
279             // version (HUNT_IO_DEBUG)
280             warningf("busy writing. fd=%d", this.handle);
281             return;
282         }
283 
284         scope(exit) {
285             _isBusyWritting = false;
286         }
287 
288         if(writeBuffer !is null) {
289             if(tryNextWrite(writeBuffer)) {
290                 writeBuffer = null;
291             } else {
292                 version (HUNT_IO_DEBUG)
293                 {
294                     infof("waiting to try again... fd=%d, writeBuffer: %s",
295                         this.handle, writeBuffer.toString());
296                 }
297                 // eventLoop.update(this);
298                 return;
299             }
300             version (HUNT_IO_DEBUG)
301                 tracef("running here, fd=%d", this.handle);
302         }
303 
304         if(checkAllWriteDone()) {
305             return;
306         }
307 
308         version (HUNT_IO_DEBUG)
309         {
310             tracef("start to write [fd=%d], writeBuffer %s empty", this.handle, writeBuffer is null ? "is" : "is not");
311         }
312 
313         if(_writeQueue.tryDequeue(writeBuffer)) {
314             if(tryNextWrite(writeBuffer)) {
315                 writeBuffer = null;
316                 checkAllWriteDone();
317             } else {
318             version (HUNT_IO_DEBUG)
319                 infof("waiting to try again: fd=%d, writeBuffer: %s", this.handle, writeBuffer.toString());
320 
321                 // eventLoop.update(this);
322             }
323             version (HUNT_IO_DEBUG)
324                 warningf("running here, fd=%d", this.handle);
325         }
326     }
327     private shared bool _isBusyWritting = false;
328 
329     protected bool checkAllWriteDone() {
330         version (HUNT_IO_DEBUG) {
331             import std.conv;
332             tracef("checking remaining: fd=%d, writeQueue empty: %s", this.handle,
333                 _writeQueue.isEmpty().to!string());
334         }
335 
336         if(_writeQueue.isEmpty()) {
337             resetWriteStatus();
338             version (HUNT_IO_DEBUG)
339                 infof("All data are written out: fd=%d", this.handle);
340             if(dataWriteDoneHandler !is null)
341                 dataWriteDoneHandler(this);
342             return true;
343         }
344 
345         return false;
346     }
347 
348     protected bool doConnect(Address addr) {
349         try {
350             this.socket.connect(addr);
351         } catch (SocketOSException e)
352         {
353             return false;
354         }
355         return true;
356     }
357 
358     void cancelWrite() {
359         isWriteCancelling = true;
360     }
361 }