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