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