00001 /******************************************************************************* 00002 00003 @file SocketConduit.d 00004 00005 Copyright (C) 2004 Kris Bell 00006 00007 This software is provided 'as-is', without any express or implied 00008 warranty. In no event will the authors be held liable for damages 00009 of any kind arising from the use of this software. 00010 00011 Permission is hereby granted to anyone to use this software for any 00012 purpose, including commercial applications, and to alter it and/or 00013 redistribute it freely, subject to the following restrictions: 00014 00015 1. The origin of this software must not be misrepresented; you must 00016 not claim that you wrote the original software. If you use this 00017 software in a product, an acknowledgment within documentation of 00018 said product would be appreciated but is not required. 00019 00020 2. Altered source versions must be plainly marked as such, and must 00021 not be misrepresented as being the original software. 00022 00023 3. This notice may not be removed or altered from any distribution 00024 of the source. 00025 00026 00027 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00028 00029 00030 @version Initial version, March 2004 00031 @author Kris 00032 00033 00034 *******************************************************************************/ 00035 00036 module mango.io.SocketConduit; 00037 00038 version = UseFreeList; 00039 00040 public import mango.io.Socket; 00041 00042 private import mango.io.Buffer, 00043 mango.io.Exception, 00044 mango.io.ConduitStyle; 00045 00046 private import mango.io.model.IConduit; 00047 00048 /******************************************************************************* 00049 00050 A wrapper around the bare Socket to implement the IConduit abstraction 00051 and add further functionality. 00052 00053 *******************************************************************************/ 00054 00055 class SocketConduit : Socket, IConduit, ISocketReader 00056 { 00057 private timeval tv; 00058 private SocketSet ss; 00059 private IConduitSource input; 00060 private IConduitSink output; 00061 00062 version (UseFreeList) 00063 { 00064 /*************************************************************** 00065 00066 Instance variables for free-list support 00067 00068 ***************************************************************/ 00069 00070 private SocketConduit next; 00071 private bool fromList; 00072 private static SocketConduit freelist; 00073 00074 /*************************************************************** 00075 00076 Allocate a SocketConduit from a list rather than 00077 creating a new one 00078 00079 ***************************************************************/ 00080 00081 private static synchronized SocketConduit allocate (socket_t sock) 00082 { 00083 SocketConduit s; 00084 00085 if (freelist) 00086 { 00087 s = freelist; 00088 freelist = s.next; 00089 s.set (sock); 00090 } 00091 else 00092 { 00093 s = new SocketConduit (sock); 00094 s.fromList = true; 00095 } 00096 return s; 00097 } 00098 00099 /*************************************************************** 00100 00101 Return this SocketConduit to the free-list 00102 00103 ***************************************************************/ 00104 00105 private static synchronized void deallocate (SocketConduit s) 00106 { 00107 // socket handle is no longer valid 00108 s.reset (); 00109 s.next = freelist; 00110 freelist = s; 00111 } 00112 00113 /*************************************************************** 00114 00115 Override closure() to deallocate this SocketConduit 00116 when it has been closed. Note that one should *not* 00117 delete a SocketConduit when FreeList is enabled ... 00118 00119 ***************************************************************/ 00120 00121 protected override void closure () 00122 { 00123 input.unbind (); 00124 output.unbind (); 00125 fixup (); 00126 00127 // be a nice client, and tell the server? 00128 //super.shutdown(); 00129 00130 // do this first cos' we're gonna' reset the 00131 // socket handle during deallocate() 00132 super.closure (); 00133 00134 // deallocate if this came from the free-list, 00135 // otherwise just wait for the GC to handle it 00136 if (fromList) 00137 deallocate (this); 00138 } 00139 } 00140 else 00141 { 00142 /*************************************************************** 00143 00144 Method to close the filters. This is invoked from the 00145 Resource base-class when the resource is being closed. 00146 You should ensure that a subclass invokes this as part 00147 of its closure mechanics. 00148 00149 ***************************************************************/ 00150 00151 protected override void closure () 00152 { 00153 input.unbind (); 00154 output.unbind (); 00155 } 00156 } 00157 00158 /*********************************************************************** 00159 00160 ***********************************************************************/ 00161 00162 private void fixup () 00163 { 00164 input = this; 00165 output = this; 00166 } 00167 00168 /*********************************************************************** 00169 00170 Construct this SocketConduit with the given socket handle; 00171 this is for FreeList and ServerSocket support. 00172 00173 ***********************************************************************/ 00174 00175 static SocketConduit create (socket_t handle) 00176 { 00177 version (UseFreeList) 00178 { 00179 // allocate one from the free-list 00180 return allocate (handle); 00181 } 00182 else 00183 { 00184 // create a new SocketConduit instance 00185 return new SocketConduit (handle); 00186 } 00187 } 00188 00189 /*********************************************************************** 00190 00191 Create a streaming Internet Socket 00192 00193 ***********************************************************************/ 00194 00195 this () 00196 { 00197 super (Socket.AddressFamily.INET, Socket.Type.STREAM, Socket.Protocol.IP); 00198 fixup (); 00199 } 00200 00201 /*********************************************************************** 00202 00203 Construct this SocketConduit with the given socket handle; 00204 this is for FreeList and ServerSocket support. 00205 00206 ***********************************************************************/ 00207 00208 private this (socket_t handle) 00209 { 00210 super (handle); 00211 fixup (); 00212 } 00213 00214 /*********************************************************************** 00215 00216 Create a Buffer of a conduit-specific size. I think 8K bytes 00217 is the common standard for socket buffering? 00218 00219 ***********************************************************************/ 00220 00221 IBuffer createBuffer () 00222 { 00223 Buffer buffer = new Buffer (1024 * 8); 00224 buffer.setConduit (this); 00225 return buffer; 00226 } 00227 00228 /*********************************************************************** 00229 00230 Set the read timeout to the specified microseconds. Set a 00231 value of zero to disable timeout support. 00232 00233 ***********************************************************************/ 00234 00235 void setTimeout (uint us) 00236 { 00237 tv.tv_sec = 0; 00238 tv.tv_usec = us; 00239 } 00240 00241 /*********************************************************************** 00242 00243 Please refer to IConduit.attach for details 00244 00245 ***********************************************************************/ 00246 00247 void attach (IConduitSource input) 00248 { 00249 input.bind (this.input); 00250 this.input = input; 00251 } 00252 00253 /*********************************************************************** 00254 00255 Please refer to IConduit.attach for details 00256 00257 ***********************************************************************/ 00258 00259 void attach (IConduitSink output) 00260 { 00261 output.bind (this.output); 00262 this.output = output; 00263 } 00264 00265 /*********************************************************************** 00266 00267 ***********************************************************************/ 00268 00269 protected void bind (IConduitSink sink) 00270 { 00271 } 00272 00273 /*********************************************************************** 00274 00275 ***********************************************************************/ 00276 00277 protected void bind (IConduitSource source) 00278 { 00279 } 00280 00281 /*********************************************************************** 00282 00283 ***********************************************************************/ 00284 00285 protected void unbind () 00286 { 00287 } 00288 00289 /*********************************************************************** 00290 00291 Callback routine to read content from the socket. Note 00292 that the operation may timeout if method setTimeout() 00293 has been invoked with a non-zero value. 00294 00295 Returns the number of bytes read from the socket, or 00296 IConduit.Eof where there's no more content available 00297 00298 ***********************************************************************/ 00299 00300 protected int reader (void[] src) 00301 { 00302 // ensure just one read at a time 00303 synchronized (_lock) 00304 { 00305 // did user disable timeout checks? 00306 if (tv.tv_usec) 00307 { 00308 // nope: ensure we have a SocketSet 00309 if (ss is null) 00310 ss = new SocketSet(1); 00311 00312 ss.reset(); 00313 ss.add(this); 00314 00315 // wait until data is available 00316 if (select (ss, null, null, &tv) <= 0) 00317 return Eof; 00318 } 00319 00320 int count = receive (src); 00321 if (count <= 0) 00322 count = Eof; 00323 return count; 00324 } 00325 } 00326 00327 /*********************************************************************** 00328 00329 Callback routine to write the provided content to the 00330 socket. This will stall until the socket responds in 00331 some manner. Returns the number of bytes sent to the 00332 output, or IConduit.Eof if the socket cannot write. 00333 00334 ***********************************************************************/ 00335 00336 protected int writer (void[] src) 00337 { 00338 int count = send (src); 00339 if (count <= 0) 00340 count = Eof; 00341 return count; 00342 } 00343 00344 /*********************************************************************** 00345 00346 Read from conduit into a target buffer. Note that this 00347 uses SocketSet to handle timeouts, such that the socket 00348 does not stall forever. 00349 00350 ***********************************************************************/ 00351 00352 int read (IBuffer target) 00353 in { 00354 assert (target); 00355 } 00356 body 00357 { 00358 return target.write (&input.reader); 00359 } 00360 00361 /*********************************************************************** 00362 00363 Write to conduit from a source buffer. 00364 00365 ***********************************************************************/ 00366 00367 int write (IBuffer source) 00368 in { 00369 assert (source); 00370 } 00371 body 00372 { 00373 int count = source.read (&output.writer); 00374 00375 // if we didn't write everything, move remaining content 00376 // to front of buffer for a subsequent write 00377 source.compress(); 00378 return count; 00379 } 00380 00381 /*********************************************************************** 00382 00383 This conduit is not seekable 00384 00385 ***********************************************************************/ 00386 00387 bool isSeekable () 00388 { 00389 return false; 00390 } 00391 00392 /*********************************************************************** 00393 00394 This conduit is readable 00395 00396 ***********************************************************************/ 00397 00398 bool isReadable () 00399 { 00400 return true; 00401 } 00402 00403 /*********************************************************************** 00404 00405 This conduit is writable 00406 00407 ***********************************************************************/ 00408 00409 bool isWritable () 00410 { 00411 return true; 00412 } 00413 00414 /*********************************************************************** 00415 00416 Transfer the content of this conduit to another one. 00417 Returns true if all content was successfully copied. 00418 00419 ***********************************************************************/ 00420 00421 IConduit copy (IConduit source) 00422 in { 00423 assert (source); 00424 } 00425 body 00426 { 00427 IBuffer buffer = createBuffer (); 00428 00429 while (source.read (buffer) != Eof) 00430 if (write (buffer) == Eof) 00431 throw new IOException ("Eof while copying to Socket"); 00432 00433 // flush any remains into the target 00434 return flush (buffer); 00435 } 00436 00437 /*********************************************************************** 00438 00439 Flush buffer content out to this conduit. 00440 00441 Returns true if all content is flushed; false if writing 00442 results in an Eof condition. 00443 00444 ***********************************************************************/ 00445 00446 IConduit flush (IBuffer source) 00447 { 00448 while (source.readable()) 00449 if (write (source) == Eof) 00450 throw new IOException ("Eof while flushing Socket"); 00451 return this; 00452 } 00453 00454 /*********************************************************************** 00455 00456 Return the style used to open the conduit 00457 00458 ***********************************************************************/ 00459 00460 ConduitStyle getStyle () 00461 { 00462 return ConduitStyle.ReadWrite; 00463 } 00464 } 00465 00466 00467