IdIOHandler.pas 75 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.123 2/8/05 5:27:06 PM RLebeau
  18. Bug fix for ReadLn().
  19. Added try..finally block to ReadLnSplit().
  20. Rev 1.122 1/27/05 3:09:30 PM RLebeau
  21. Updated AllData() to call ReadFromSource() directly instead of using
  22. CheckForDataOnSource(), since ReadFromSource() can return a disconnect
  23. conditon. When data is in the InputBuffer, Connected() always return True
  24. even if the socket is actually disconnected.
  25. Rev 1.121 12/21/04 3:21:40 AM RLebeau
  26. Removed compiler warning
  27. Rev 1.120 17/12/2004 17:11:28 ANeillans
  28. Compiler fix
  29. Rev 1.119 12/12/04 2:23:52 PM RLebeau
  30. Added WriteRFCStrings() method
  31. Rev 1.118 12/11/2004 9:04:50 PM DSiders
  32. Fixed comparison error in WaitFor.
  33. Rev 1.117 12/10/04 2:00:24 PM RLebeau
  34. Updated WaitFor() to not return more data than actually needed.
  35. Updated AllData() to not concatenate the Result on every iteration of the
  36. loop.
  37. Rev 1.116 11/29/04 10:37:18 AM RLebeau
  38. Updated write buffering methods to prevent Access Violations when used
  39. incorrectly.
  40. Rev 1.115 11/4/04 12:41:08 PM RLebeau
  41. Bug fix for ReadLn()
  42. Rev 1.114 10/26/2004 8:43:00 PM JPMugaas
  43. Should be more portable with new references to TIdStrings and TIdStringList.
  44. Rev 1.113 27.08.2004 21:58:18 Andreas Hausladen
  45. Speed optimization ("const" for string parameters)
  46. Rev 1.112 8/2/04 5:49:20 PM RLebeau
  47. Moved ConnectTimeout over from TIdIOHandlerSocket
  48. Rev 1.111 2004.08.01 19:36:14 czhower
  49. Code optimization to WriteFile
  50. Rev 1.110 7/24/04 12:53:54 PM RLebeau
  51. Compiler fix for WriteFile()
  52. Rev 1.109 7/23/04 6:39:14 PM RLebeau
  53. Added extra exception handling to WriteFile()
  54. Rev 1.108 7/21/2004 5:45:10 PM JPMugaas
  55. Updated with Remy's change. This should work better and fix a problem with
  56. looping with ReadStream and ReadUntilDisconnect.
  57. Rev 1.107 7/21/2004 12:22:18 PM BGooijen
  58. Reverted back 2 versions
  59. Rev 1.104 6/29/04 12:16:16 PM RLebeau
  60. Updated ReadChar() to call ReadBytes() directly instead of ReadString()
  61. Rev 1.103 6/17/04 3:01:56 PM RLebeau
  62. Changed ReadStream() to not extract too many bytes from the InputBuffer when
  63. an error occurs
  64. Rev 1.102 6/12/04 11:36:44 AM RLebeau
  65. Changed ReadString() to pass the ABytes parameter to ReadBytes() instead of
  66. the LBuf length
  67. Rev 1.100 6/10/2004 6:52:12 PM JPMugaas
  68. Regeneration to fix a bug in the package generator that I created. OOPS!!!
  69. Rev 1.99 6/9/04 7:36:26 PM RLebeau
  70. ReadString() bug fix
  71. Rev 1.98 07/06/2004 20:55:36 CCostelloe
  72. Fix for possible memory leak.
  73. Rev 1.97 5/29/04 10:46:24 PM RLebeau
  74. Updated AllData() to only append values to the result when there is actual
  75. data in the buffer.
  76. Rev 1.96 29/05/2004 21:07:40 CCostelloe
  77. Bug fix (may need more investigation)
  78. Rev 1.95 2004.05.20 1:39:54 PM czhower
  79. Last of the IdStream updates
  80. Rev 1.94 2004.05.20 12:34:22 PM czhower
  81. Removed more non .NET compatible stream read and writes
  82. Rev 1.93 2004.05.20 11:39:02 AM czhower
  83. IdStreamVCL
  84. Rev 1.92 5/3/2004 12:57:00 PM BGooijen
  85. Fixes for 0-based
  86. Rev 1.91 2004.05.03 11:15:44 AM czhower
  87. Changed Find to IndexOf and made 0 based to be consistent.
  88. Rev 1.90 4/24/04 12:40:04 PM RLebeau
  89. Added Write() overload for Char type.
  90. Rev 1.89 4/18/2004 11:58:00 PM BGooijen
  91. ReadBytes with count=-1 reads everything available, ( and waits ReadTimeOut
  92. time for data)
  93. Rev 1.88 4/18/04 2:44:24 PM RLebeau
  94. Read/write support for Int64 values
  95. Rev 1.87 2004.04.18 12:51:58 AM czhower
  96. Big bug fix with server disconnect and several other bug fixed that I found
  97. along the way.
  98. Rev 1.86 2004.04.16 11:30:28 PM czhower
  99. Size fix to IdBuffer, optimizations, and memory leaks
  100. Rev 1.85 2004.04.08 7:06:46 PM czhower
  101. Peek support.
  102. Rev 1.84 2004.04.08 3:56:28 PM czhower
  103. Fixed bug with Intercept byte count. Also removed Bytes from Buffer.
  104. Rev 1.83 2004.04.08 2:08:00 AM czhower
  105. Saved before checkin this time...
  106. Rev 1.82 7/4/2004 4:08:46 PM SGrobety
  107. Re-introduce the IOHandler.MaxCapturedLines property
  108. Rev 1.81 2004.04.07 3:59:46 PM czhower
  109. Bug fix for WriteDirect.
  110. Rev 1.79 2004.03.07 11:48:38 AM czhower
  111. Flushbuffer fix + other minor ones found
  112. Rev 1.78 2004.03.03 11:54:58 AM czhower
  113. IdStream change
  114. Rev 1.77 2004.03.02 2:47:08 PM czhower
  115. .Net overloads
  116. Rev 1.76 2004.03.01 5:12:28 PM czhower
  117. -Bug fix for shutdown of servers when connections still existed (AV)
  118. -Implicit HELP support in CMDserver
  119. -Several command handler bugs
  120. -Additional command handler functionality.
  121. Rev 1.75 2004.02.03 4:16:44 PM czhower
  122. For unit name changes.
  123. Rev 1.74 2004.01.21 9:36:00 PM czhower
  124. .Net overload
  125. Rev 1.73 2004.01.21 12:19:58 AM czhower
  126. .Readln overload for .net
  127. Rev 1.72 2004.01.20 10:03:26 PM czhower
  128. InitComponent
  129. Rev 1.71 1/11/2004 5:51:04 PM BGooijen
  130. Added AApend parameter to ReadBytes
  131. Rev 1.70 12/30/2003 7:17:56 PM BGooijen
  132. .net
  133. Rev 1.69 2003.12.28 1:05:54 PM czhower
  134. .Net changes.
  135. Rev 1.68 2003.12.28 11:53:28 AM czhower
  136. Removed warning in .net.
  137. Rev 1.67 2003.11.29 10:15:30 AM czhower
  138. InternalBuffer --> InputBuffer for consistency.
  139. Rev 1.66 11/23/03 1:46:28 PM RLebeau
  140. Removed "var" specifier from TStrings parameter of ReadStrings().
  141. Rev 1.65 11/4/2003 10:27:56 PM DSiders
  142. Removed exceptions moved to IdException.pas.
  143. Rev 1.64 2003.10.24 10:44:52 AM czhower
  144. IdStream implementation, bug fixes.
  145. Rev 1.63 10/22/03 2:05:40 PM RLebeau
  146. Fix for TIdIOHandler::Write(TStream) where it was not reading the stream into
  147. the TIdBytes correctly.
  148. Rev 1.62 10/19/2003 5:55:44 PM BGooijen
  149. Fixed todo in PerformCapture
  150. Rev 1.61 2003.10.18 12:58:50 PM czhower
  151. Added comment
  152. Rev 1.60 2003.10.18 12:42:04 PM czhower
  153. Intercept.Disconnect is now called
  154. Rev 1.59 10/15/2003 7:39:28 PM DSiders
  155. Added a formatted resource string for the exception raised in
  156. TIdIOHandler.MakeIOHandler.
  157. Rev 1.58 2003.10.14 1:26:50 PM czhower
  158. Uupdates + Intercept support
  159. Rev 1.57 2003.10.11 5:48:22 PM czhower
  160. -VCL fixes for servers
  161. -Chain suport for servers (Super core)
  162. -Scheduler upgrades
  163. -Full yarn support
  164. Rev 1.56 9/10/2003 1:50:38 PM SGrobety
  165. Removed all "const" keywords from boolean parameter interfaces. Might trigger
  166. changes in other units.
  167. Rev 1.55 10/5/2003 10:39:56 PM BGooijen
  168. Write buffering
  169. Rev 1.54 10/4/2003 11:03:12 PM BGooijen
  170. ReadStream, and functions with network ordering
  171. Rev 1.53 10/4/2003 7:10:46 PM BGooijen
  172. ReadXXXXX
  173. Rev 1.52 10/4/2003 3:55:02 PM BGooijen
  174. ReadString, and some Write functions
  175. Rev 1.51 04/10/2003 13:38:32 HHariri
  176. Write(Integer) support
  177. Rev 1.50 10/3/2003 12:09:30 AM BGooijen
  178. DotNet
  179. Rev 1.49 2003.10.02 8:29:14 PM czhower
  180. Changed names of byte conversion routines to be more readily understood and
  181. not to conflict with already in use ones.
  182. Rev 1.48 2003.10.02 1:18:50 PM czhower
  183. Changed read methods to be overloaded and more consistent. Will break some
  184. code, but nearly all code that uses them is Input.
  185. Rev 1.47 2003.10.02 10:16:26 AM czhower
  186. .Net
  187. Rev 1.46 2003.10.01 9:11:16 PM czhower
  188. .Net
  189. Rev 1.45 2003.10.01 2:46:36 PM czhower
  190. .Net
  191. Rev 1.42 2003.10.01 11:16:32 AM czhower
  192. .Net
  193. Rev 1.41 2003.10.01 1:37:34 AM czhower
  194. .Net
  195. Rev 1.40 2003.10.01 1:12:34 AM czhower
  196. .Net
  197. Rev 1.39 2003.09.30 1:22:56 PM czhower
  198. Stack split for DotNet
  199. Rev 1.38 2003.09.18 5:17:58 PM czhower
  200. Implemented OnWork
  201. Rev 1.37 2003.08.21 10:43:42 PM czhower
  202. Fix to ReadStream from Doychin
  203. Rev 1.36 08/08/2003 17:32:26 CCostelloe
  204. Removed "virtual" from function ReadLnSplit
  205. Rev 1.35 07/08/2003 00:25:08 CCostelloe
  206. Function ReadLnSplit added
  207. Rev 1.34 2003.07.17 1:05:12 PM czhower
  208. More IOCP improvements.
  209. Rev 1.33 2003.07.14 11:00:50 PM czhower
  210. More IOCP fixes.
  211. Rev 1.32 2003.07.14 12:54:30 AM czhower
  212. Fixed graceful close detection if it occurs after connect.
  213. Rev 1.31 2003.07.10 7:40:24 PM czhower
  214. Comments
  215. Rev 1.30 2003.07.10 4:34:56 PM czhower
  216. Fixed AV, added some new comments
  217. Rev 1.29 7/1/2003 5:50:44 PM BGooijen
  218. Fixed ReadStream
  219. Rev 1.28 6/30/2003 10:26:08 AM BGooijen
  220. forgot to remove some code regarding to TIdBuffer.Find
  221. Rev 1.27 6/29/2003 10:56:26 PM BGooijen
  222. Removed .Memory from the buffer, and added some extra methods
  223. Rev 1.26 2003.06.25 4:30:00 PM czhower
  224. Temp hack fix for AV problem. Working on real solution now.
  225. Rev 1.25 23/6/2003 22:33:14 GGrieve
  226. fix CheckForDataOnSource - specify timeout
  227. Rev 1.24 23/6/2003 06:46:52 GGrieve
  228. allow block on checkForData
  229. Rev 1.23 6/4/2003 1:07:08 AM BGooijen
  230. changed comment
  231. Rev 1.22 6/3/2003 10:40:34 PM BGooijen
  232. FRecvBuffer bug fixed, it was freed, but never recreated, resulting in an AV
  233. Rev 1.21 2003.06.03 6:28:04 PM czhower
  234. Made check for data virtual
  235. Rev 1.20 2003.06.03 3:43:24 PM czhower
  236. Resolved InputBuffer inconsistency. Added new method and renamed old one.
  237. Rev 1.19 5/25/2003 03:56:04 AM JPMugaas
  238. Updated for unit rename.
  239. Rev 1.18 2003.04.17 11:01:12 PM czhower
  240. Rev 1.17 4/16/2003 3:29:30 PM BGooijen
  241. minor change in ReadBuffer
  242. Rev 1.16 4/1/2003 7:54:24 PM BGooijen
  243. ReadLn default terminator changed to LF
  244. Rev 1.15 3/27/2003 3:24:06 PM BGooijen
  245. MaxLine* is now published
  246. Rev 1.14 2003.03.25 7:42:12 PM czhower
  247. try finally to WriteStrings
  248. Rev 1.13 3/24/2003 11:01:36 PM BGooijen
  249. WriteStrings is now buffered to increase speed
  250. Rev 1.12 3/19/2003 1:02:32 PM BGooijen
  251. changed class function ConstructDefaultIOHandler a little (default parameter)
  252. Rev 1.11 3/13/2003 10:18:16 AM BGooijen
  253. Server side fibers, bug fixes
  254. Rev 1.10 3/5/2003 11:03:06 PM BGooijen
  255. Added Intercept here
  256. Rev 1.9 2/25/2003 11:02:12 PM BGooijen
  257. InputBufferToStream now accepts a bytecount
  258. Rev 1.8 2003.02.25 1:36:00 AM czhower
  259. Rev 1.7 12-28-2002 22:28:16 BGooijen
  260. removed warning, added initialization and finalization part.
  261. Rev 1.6 12-16-2002 20:43:28 BGooijen
  262. Added class function ConstructIOHandler(....), and removed some comments
  263. Rev 1.5 12-15-2002 23:02:38 BGooijen
  264. added SendBufferSize
  265. Rev 1.4 12-15-2002 20:50:32 BGooijen
  266. FSendBufferSize was not initialized
  267. Rev 1.3 12-14-2002 22:14:54 BGooijen
  268. improved method to detect timeouts in ReadLn.
  269. Rev 1.2 12/11/2002 04:09:28 AM JPMugaas
  270. Updated for new API.
  271. Rev 1.1 2002.12.07 12:25:56 AM czhower
  272. Rev 1.0 11/13/2002 08:44:50 AM JPMugaas
  273. }
  274. unit IdIOHandler;
  275. interface
  276. {$I IdCompilerDefines.inc}
  277. uses
  278. Classes,
  279. IdException,
  280. IdAntiFreezeBase, IdBuffer, IdBaseComponent, IdComponent, IdGlobal, IdExceptionCore,
  281. IdIntercept, IdResourceStringsCore;
  282. (*$HPPEMIT '#if defined(_VCL_ALIAS_RECORDS)' *)
  283. (*$HPPEMIT '#if !defined(UNICODE)' *)
  284. (*$HPPEMIT '#pragma alias "@Idiohandler@TIdIOHandler@SetPortA$qqri"="@Idiohandler@TIdIOHandler@SetPort$qqri"' *)
  285. (*$HPPEMIT '#else' *)
  286. (*$HPPEMIT '#pragma alias "@Idiohandler@TIdIOHandler@SetPortW$qqri"="@Idiohandler@TIdIOHandler@SetPort$qqri"' *)
  287. (*$HPPEMIT '#endif' *)
  288. (*$HPPEMIT '#endif' *)
  289. // TODO: when compiling with bcc64x, use this pragma instead:
  290. // #pragma comment(linker, "/alternatename:<name1>=<name2>")
  291. const
  292. GRecvBufferSizeDefault = 32 * 1024;
  293. GSendBufferSizeDefault = 32 * 1024;
  294. IdMaxLineLengthDefault = 16 * 1024;
  295. // S.G. 6/4/2004: Maximum number of lines captured
  296. // S.G. 6/4/2004: Default to "unlimited"
  297. Id_IOHandler_MaxCapturedLines = -1;
  298. type
  299. EIdIOHandler = class(EIdException);
  300. EIdIOHandlerRequiresLargeStream = class(EIdIOHandler);
  301. EIdIOHandlerStreamDataTooLarge = class(EIdIOHandler);
  302. TIdIOHandlerClass = class of TIdIOHandler;
  303. {
  304. How does this fit in in the hierarchy against TIdIOHandlerSocket
  305. Destination - Socket - otehr file descendats it
  306. TIdIOHandler should only implement an interface. No default functionality
  307. except very simple read/write functions such as ReadUInt32, etc. Functions
  308. that cannot really be optimized beyond their default implementations.
  309. Some default implementations offer basic non optmized implementations.
  310. Yes, I know this comment conflicts. Its being worked on.
  311. }
  312. TIdIOHandler = class(TIdComponent)
  313. private
  314. FLargeStream: Boolean;
  315. procedure EnsureInputBytes(AByteCount: Integer);
  316. protected
  317. FClosedGracefully: Boolean;
  318. FConnectTimeout: Integer;
  319. FDestination: string;
  320. FHost: string;
  321. // IOHandlers typically receive more data than they need to complete each
  322. // request. They store this extra data in InputBuffer for future methods to
  323. // use. InputBuffer is what collects the input and keeps it if the current
  324. // method does not need all of it.
  325. //
  326. FInputBuffer: TIdBuffer;
  327. {$IF DEFINED(HAS_UNSAFE_OBJECT_REF)}[Unsafe]
  328. {$ELSEIF DEFINED(HAS_WEAK_OBJECT_REF)}[Weak]
  329. {$IFEND} FIntercept: TIdConnectionIntercept;
  330. FMaxCapturedLines: Integer;
  331. FMaxLineAction: TIdMaxLineAction;
  332. FMaxLineLength: Integer;
  333. FOpened: Boolean;
  334. FPort: Integer;
  335. FReadLnSplit: Boolean;
  336. FReadLnTimedOut: Boolean;
  337. FReadTimeOut: Integer;
  338. //TODO:
  339. FRecvBufferSize: Integer;
  340. FSendBufferSize: Integer;
  341. FWriteBuffer: TIdBuffer;
  342. FWriteBufferThreshold: Integer;
  343. FDefStringEncoding : IIdTextEncoding;
  344. procedure SetDefStringEncoding(const AEncoding : IIdTextEncoding);
  345. //
  346. procedure BufferRemoveNotify(ASender: TObject; ABytes: Integer);
  347. function GetDestination: string; virtual;
  348. procedure InterceptReceive(var VBuffer: TIdBytes);
  349. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  350. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  351. {$ENDIF}
  352. procedure PerformCapture(const ADest: TObject; out VLineCount: Integer;
  353. const ADelim: string; AUsesDotTransparency: Boolean; AByteEncoding: IIdTextEncoding = nil); virtual;
  354. procedure RaiseConnClosedGracefully;
  355. procedure SetDestination(const AValue: string); virtual;
  356. procedure SetHost(const AValue: string); virtual;
  357. procedure SetPort(AValue: Integer); virtual;
  358. procedure SetIntercept(AValue: TIdConnectionIntercept); virtual;
  359. // This is the main Read function which all other default implementations
  360. // use.
  361. function ReadFromSource(ARaiseExceptionIfDisconnected: Boolean = True;
  362. ATimeout: Integer = IdTimeoutDefault;
  363. ARaiseExceptionOnTimeout: Boolean = True): Integer;
  364. function ReadDataFromSource(var VBuffer: TIdBytes): Integer; virtual; abstract;
  365. function WriteDataToTarget(const ABuffer: TIdBytes; const AOffset, ALength: Integer): Integer; virtual; abstract;
  366. function SourceIsAvailable: Boolean; virtual; abstract;
  367. function CheckForError(ALastResult: Integer): Integer; virtual; abstract;
  368. procedure RaiseError(AError: Integer); virtual; abstract;
  369. public
  370. constructor Create(AOwner: TComponent); override;
  371. destructor Destroy; override;
  372. procedure AfterAccept; virtual;
  373. function Connected: Boolean; virtual;
  374. // CheckForDisconnect allows the implementation to check the status of the
  375. // connection at the request of the user or this base class.
  376. procedure CheckForDisconnect(ARaiseExceptionIfDisconnected: Boolean = True;
  377. AIgnoreBuffer: Boolean = False); virtual; abstract;
  378. // Does not wait or raise any exceptions. Just reads whatever data is
  379. // available (if any) into the buffer. Must NOT raise closure exceptions.
  380. // It is used to get avialable data, and check connection status. That is
  381. // it can set status flags about the connection.
  382. function CheckForDataOnSource(ATimeout: Integer = 0): Boolean; virtual;
  383. procedure Close; virtual;
  384. procedure CloseGracefully; virtual;
  385. class function MakeDefaultIOHandler(AOwner: TComponent = nil)
  386. : TIdIOHandler;
  387. class function MakeIOHandler(ABaseType: TIdIOHandlerClass;
  388. AOwner: TComponent = nil): TIdIOHandler;
  389. // Variant of MakeIOHandler() which returns nil if it cannot find a registered IOHandler
  390. class function TryMakeIOHandler(ABaseType: TIdIOHandlerClass;
  391. AOwner: TComponent = nil): TIdIOHandler;
  392. class procedure RegisterIOHandler;
  393. class procedure SetDefaultClass;
  394. function WaitFor(const AString: string; ARemoveFromBuffer: Boolean = True;
  395. AInclusive: Boolean = False; AByteEncoding: IIdTextEncoding = nil;
  396. ATimeout: Integer = IdTimeoutDefault): string;
  397. // This is different than WriteDirect. WriteDirect goes
  398. // directly to the network or next level. WriteBuffer allows for buffering
  399. // using WriteBuffers. This should be the only call to WriteDirect
  400. // unless the calls that bypass this are aware of WriteBuffering or are
  401. // intended to bypass it.
  402. procedure Write(const ABuffer: TIdBytes; const ALength: Integer = -1; const AOffset: Integer = 0); overload; virtual;
  403. // This is the main write function which all other default implementations
  404. // use. If default implementations are used, this must be implemented.
  405. procedure WriteDirect(const ABuffer: TIdBytes; const ALength: Integer = -1; const AOffset: Integer = 0);
  406. //
  407. procedure Open; virtual;
  408. function Readable(AMSec: Integer = IdTimeoutDefault): Boolean; virtual;
  409. //
  410. // Optimal Extra Methods
  411. //
  412. // These methods are based on the core methods. While they can be
  413. // overridden, they are so simple that it is rare a more optimal method can
  414. // be implemented. Because of this they are not overrideable.
  415. //
  416. //
  417. // Write Methods
  418. //
  419. // Only the ones that have a hope of being better optimized in descendants
  420. // have been marked virtual
  421. procedure Write(const AOut: string; AByteEncoding: IIdTextEncoding = nil); overload; virtual;
  422. procedure WriteLn(AEncoding: IIdTextEncoding = nil); overload;
  423. procedure WriteLn(const AOut: string; AByteEncoding: IIdTextEncoding = nil); overload; virtual;
  424. procedure WriteLnRFC(const AOut: string = ''; AByteEncoding: IIdTextEncoding = nil); virtual;
  425. procedure Write(AValue: TStrings; AWriteLinesCount: Boolean = False; AByteEncoding: IIdTextEncoding = nil); overload; virtual;
  426. procedure Write(AValue: Byte); overload;
  427. procedure Write(AValue: Char; AByteEncoding: IIdTextEncoding = nil); overload;
  428. // for iOS64, Delphi's Longint and LongWord are 64bit, so we can't rely on
  429. // Write(Longint) and ReadLongint() being 32bit anymore, for instance when
  430. // sending/reading a TStream with LargeStream=False. So adding new (U)IntX
  431. // methods and deprecating the old ones...
  432. //
  433. procedure Write(AValue: Int16; AConvert: Boolean = True); overload;
  434. procedure Write(AValue: UInt16; AConvert: Boolean = True); overload;
  435. procedure Write(AValue: Int32; AConvert: Boolean = True); overload;
  436. procedure Write(AValue: UInt32; AConvert: Boolean = True); overload;
  437. procedure Write(AValue: Int64; AConvert: Boolean = True); overload;
  438. procedure Write(AValue: UInt64; AConvert: Boolean = True); overload;
  439. //
  440. procedure Write(AStream: TStream; ASize: Int64 = 0;
  441. AWriteByteCount: Boolean = False); overload; virtual;
  442. procedure WriteRFCStrings(AStrings: TStrings; AWriteTerminator: Boolean = True; AByteEncoding: IIdTextEncoding = nil);
  443. // Not overloaded because it does not have a unique type for source
  444. // and could be easily unresolvable with future additions
  445. function WriteFile(const AFile: String; AEnableTransferFile: Boolean = False): Int64; virtual;
  446. //
  447. // Read methods
  448. //
  449. function AllData(AByteEncoding: IIdTextEncoding = nil): string; virtual;
  450. function InputLn(const AMask: string = ''; AEcho: Boolean = True;
  451. ATabWidth: Integer = 8; AMaxLineLength: Integer = -1;
  452. AByteEncoding: IIdTextEncoding = nil): string; virtual;
  453. // Capture
  454. // Not virtual because each calls PerformCapture which is virtual
  455. procedure Capture(ADest: TStream; AByteEncoding: IIdTextEncoding = nil); overload; // .Net overload
  456. procedure Capture(ADest: TStream; ADelim: string;
  457. AUsesDotTransparency: Boolean = True; AByteEncoding: IIdTextEncoding = nil); overload;
  458. procedure Capture(ADest: TStream; out VLineCount: Integer;
  459. const ADelim: string = '.'; AUsesDotTransparency: Boolean = True;
  460. AByteEncoding: IIdTextEncoding = nil); overload;
  461. procedure Capture(ADest: TStrings; AByteEncoding: IIdTextEncoding = nil); overload; // .Net overload
  462. procedure Capture(ADest: TStrings; const ADelim: string;
  463. AUsesDotTransparency: Boolean = True; AByteEncoding: IIdTextEncoding = nil); overload;
  464. procedure Capture(ADest: TStrings; out VLineCount: Integer;
  465. const ADelim: string = '.'; AUsesDotTransparency: Boolean = True;
  466. AByteEncoding: IIdTextEncoding = nil); overload;
  467. //
  468. // Read___
  469. // Cannot overload, compiler cannot overload on return values
  470. //
  471. procedure ReadBytes(var VBuffer: TIdBytes; AByteCount: Integer; AAppend: Boolean = True); virtual;
  472. // ReadLn
  473. function ReadLn(AByteEncoding: IIdTextEncoding = nil): string; overload; // .Net overload
  474. function ReadLn(ATerminator: string; AByteEncoding: IIdTextEncoding): string; overload;
  475. function ReadLn(ATerminator: string; ATimeout: Integer = IdTimeoutDefault;
  476. AMaxLineLength: Integer = -1; AByteEncoding: IIdTextEncoding = nil): string; overload; virtual;
  477. //RLebeau: added for RFC 822 retrieves
  478. function ReadLnRFC(var VMsgEnd: Boolean; AByteEncoding: IIdTextEncoding = nil): string; overload;
  479. function ReadLnRFC(var VMsgEnd: Boolean; const ALineTerminator: string;
  480. const ADelim: string = '.'; AByteEncoding: IIdTextEncoding = nil): string; overload;
  481. function ReadLnWait(AFailCount: Integer = MaxInt; AByteEncoding: IIdTextEncoding = nil): string; virtual;
  482. // Added for retrieving lines over 16K long}
  483. function ReadLnSplit(var AWasSplit: Boolean; ATerminator: string = LF;
  484. ATimeout: Integer = IdTimeoutDefault; AMaxLineLength: Integer = -1;
  485. AByteEncoding: IIdTextEncoding = nil): string;
  486. // Read - Simple Types
  487. function ReadChar(AByteEncoding: IIdTextEncoding = nil): Char;
  488. function ReadByte: Byte;
  489. function ReadString(ABytes: Integer; AByteEncoding: IIdTextEncoding = nil): string;
  490. // for iOS64, Delphi's Longint and LongWord are changed to 64bit, so we can't
  491. // rely on Write(Longint) and ReadLongint() being 32bit anymore, for instance
  492. // when sending/reading a TStream with LargeStream=False. So adding new (U)IntX
  493. // methods and deprecating the old ones...
  494. //
  495. function ReadInt16(AConvert: Boolean = True): Int16;
  496. function ReadUInt16(AConvert: Boolean = True): UInt16;
  497. function ReadInt32(AConvert: Boolean = True): Int32;
  498. function ReadUInt32(AConvert: Boolean = True): UInt32;
  499. function ReadInt64(AConvert: Boolean = True): Int64;
  500. function ReadUInt64(AConvert: Boolean = True): UInt64;
  501. //
  502. procedure ReadStream(AStream: TStream; AByteCount: Int64 = -1; AReadUntilDisconnect: Boolean = False); virtual;
  503. procedure ReadStrings(ADest: TStrings; AReadLinesCount: Integer = -1; AByteEncoding: IIdTextEncoding = nil);
  504. //
  505. procedure Discard(AByteCount: Int64);
  506. procedure DiscardAll;
  507. //
  508. // WriteBuffering Methods
  509. //
  510. procedure WriteBufferCancel; virtual;
  511. procedure WriteBufferClear; virtual;
  512. procedure WriteBufferClose; virtual;
  513. procedure WriteBufferFlush; overload; //.Net overload
  514. procedure WriteBufferFlush(AByteCount: Integer); overload; virtual;
  515. procedure WriteBufferOpen; overload; //.Net overload
  516. procedure WriteBufferOpen(AThreshold: Integer); overload; virtual;
  517. function WriteBufferingActive: Boolean;
  518. //
  519. // InputBuffer Methods
  520. //
  521. function InputBufferIsEmpty: Boolean;
  522. //
  523. // These two are direct access and do no reading of connection
  524. procedure InputBufferToStream(AStream: TStream; AByteCount: Integer = -1);
  525. function InputBufferAsString(AByteEncoding: IIdTextEncoding = nil): string;
  526. //
  527. // Properties
  528. //
  529. property ConnectTimeout: Integer read FConnectTimeout write FConnectTimeout default 0;
  530. property ClosedGracefully: Boolean read FClosedGracefully;
  531. // TODO: Need to name this consistent. Originally no access was allowed,
  532. // but new model requires it for writing. Will decide after next set
  533. // of changes are complete what to do with Buffer prop.
  534. //
  535. // Is used by SuperCore
  536. property InputBuffer: TIdBuffer read FInputBuffer;
  537. //currently an option, as LargeFile support changes the data format
  538. property LargeStream: Boolean read FLargeStream write FLargeStream;
  539. property MaxCapturedLines: Integer read FMaxCapturedLines write FMaxCapturedLines default Id_IOHandler_MaxCapturedLines;
  540. property Opened: Boolean read FOpened;
  541. property ReadTimeout: Integer read FReadTimeOut write FReadTimeOut default IdTimeoutDefault;
  542. property ReadLnTimedout: Boolean read FReadLnTimedout ;
  543. property WriteBufferThreshold: Integer read FWriteBufferThreshold;
  544. property DefStringEncoding : IIdTextEncoding read FDefStringEncoding write SetDefStringEncoding;
  545. //
  546. // Events
  547. //
  548. property OnWork;
  549. property OnWorkBegin;
  550. property OnWorkEnd;
  551. published
  552. property Destination: string read GetDestination write SetDestination;
  553. property Host: string read FHost write SetHost;
  554. property Intercept: TIdConnectionIntercept read FIntercept write SetIntercept;
  555. property MaxLineLength: Integer read FMaxLineLength write FMaxLineLength default IdMaxLineLengthDefault;
  556. property MaxLineAction: TIdMaxLineAction read FMaxLineAction write FMaxLineAction;
  557. property Port: Integer read FPort write SetPort;
  558. // RecvBufferSize is used by some methods that read large amounts of data.
  559. // RecvBufferSize is the amount of data that will be requested at each read
  560. // cycle. RecvBuffer is used to receive then send to the Intercepts, after
  561. // that it goes to InputBuffer
  562. property RecvBufferSize: Integer read FRecvBufferSize write FRecvBufferSize
  563. default GRecvBufferSizeDefault;
  564. // SendBufferSize is used by some methods that have to break apart large
  565. // amounts of data into smaller pieces. This is the buffer size of the
  566. // chunks that it will create and use.
  567. property SendBufferSize: Integer read FSendBufferSize write FSendBufferSize
  568. default GSendBufferSizeDefault;
  569. end;
  570. implementation
  571. uses
  572. //facilitate inlining only.
  573. {$IF DEFINED(WIN32_OR_WIN64)}
  574. Windows,
  575. {$ELSEIF DEFINED(USE_VCL_POSIX) AND DEFINED(OSX)}
  576. Macapi.CoreServices,
  577. {$IFEND}
  578. {$IFDEF HAS_UNIT_Generics_Collections}
  579. System.Generics.Collections,
  580. {$ENDIF}
  581. IdStack, IdStackConsts, IdResourceStrings,
  582. SysUtils;
  583. type
  584. {$IFDEF HAS_GENERICS_TList}
  585. TIdIOHandlerClassList = TList<TIdIOHandlerClass>;
  586. {$ELSE}
  587. // TODO: flesh out to match TList<TIdIOHandlerClass> for non-Generics compilers
  588. TIdIOHandlerClassList = TList;
  589. {$ENDIF}
  590. var
  591. GIOHandlerClassDefault: TIdIOHandlerClass = nil;
  592. GIOHandlerClassList: TIdIOHandlerClassList = nil;
  593. { TIdIOHandler }
  594. constructor TIdIOHandler.Create(AOwner: TComponent);
  595. begin
  596. inherited Create(AOwner);
  597. FRecvBufferSize := GRecvBufferSizeDefault;
  598. FSendBufferSize := GSendBufferSizeDefault;
  599. FMaxLineLength := IdMaxLineLengthDefault;
  600. FMaxCapturedLines := Id_IOHandler_MaxCapturedLines;
  601. FLargeStream := False;
  602. FReadTimeOut := IdTimeoutDefault;
  603. FInputBuffer := TIdBuffer.Create(BufferRemoveNotify);
  604. FDefStringEncoding := IndyTextEncoding_ASCII; // TODO: use IndyTextEncoding_Default instead...
  605. end;
  606. destructor TIdIOHandler.Destroy;
  607. begin
  608. Close;
  609. FInputBuffer.Free;
  610. FWriteBuffer.Free;
  611. inherited Destroy;
  612. end;
  613. procedure TIdIOHandler.Close;
  614. //do not do FInputBuffer.Clear; here.
  615. //it breaks reading when remote connection does a disconnect
  616. var
  617. // under ARC, convert a weak reference to a strong reference before working with it
  618. LIntercept: TIdConnectionIntercept;
  619. begin
  620. try
  621. LIntercept := Intercept;
  622. if LIntercept <> nil then begin
  623. LIntercept.Disconnect;
  624. end;
  625. finally
  626. FOpened := False;
  627. WriteBufferClear;
  628. end;
  629. end;
  630. procedure TIdIOHandler.AfterAccept;
  631. begin
  632. //
  633. end;
  634. procedure TIdIOHandler.Open;
  635. begin
  636. FOpened := False;
  637. FClosedGracefully := False;
  638. WriteBufferClear;
  639. FInputBuffer.Clear;
  640. FOpened := True;
  641. end;
  642. // under ARC, all weak references to a freed object get nil'ed automatically
  643. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  644. procedure TIdIOHandler.Notification(AComponent: TComponent; Operation: TOperation);
  645. begin
  646. if (Operation = opRemove) and (AComponent = FIntercept) then begin
  647. FIntercept := nil;
  648. end;
  649. inherited Notification(AComponent, OPeration);
  650. end;
  651. {$ENDIF}
  652. // RLebeau: not IFDEF'ing the entire method since it is virtual and could be
  653. // overridden in user code...
  654. procedure TIdIOHandler.SetIntercept(AValue: TIdConnectionIntercept);
  655. begin
  656. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  657. if FIntercept <> AValue then begin
  658. // remove self from the Intercept's free notification list
  659. if Assigned(FIntercept) then begin
  660. FIntercept.RemoveFreeNotification(Self);
  661. end;
  662. FIntercept := AValue;
  663. // add self to the Intercept's free notification list
  664. if Assigned(AValue) then begin
  665. AValue.FreeNotification(Self);
  666. end;
  667. end;
  668. {$ELSE}
  669. // under ARC, all weak references to a freed object get nil'ed automatically
  670. FIntercept := AValue;
  671. {$ENDIF}
  672. end;
  673. class procedure TIdIOHandler.SetDefaultClass;
  674. begin
  675. GIOHandlerClassDefault := Self;
  676. RegisterIOHandler;
  677. end;
  678. procedure TIdIOHandler.SetDefStringEncoding(const AEncoding: IIdTextEncoding);
  679. var
  680. LEncoding: IIdTextEncoding;
  681. begin
  682. if FDefStringEncoding <> AEncoding then
  683. begin
  684. LEncoding := AEncoding;
  685. EnsureEncoding(LEncoding);
  686. FDefStringEncoding := LEncoding;
  687. end;
  688. end;
  689. class function TIdIOHandler.MakeDefaultIOHandler(AOwner: TComponent = nil): TIdIOHandler;
  690. begin
  691. Result := GIOHandlerClassDefault.Create(AOwner);
  692. end;
  693. class procedure TIdIOHandler.RegisterIOHandler;
  694. begin
  695. if GIOHandlerClassList = nil then begin
  696. GIOHandlerClassList := TIdIOHandlerClassList.Create;
  697. end;
  698. //TODO: Reenable this. Dot net wont allow class references as objects
  699. // Use an array?
  700. if GIOHandlerClassList.IndexOf(Self) = -1 then begin
  701. GIOHandlerClassList.Add(Self);
  702. end;
  703. end;
  704. {
  705. Creates an IOHandler of type ABaseType, or descendant.
  706. }
  707. class function TIdIOHandler.MakeIOHandler(ABaseType: TIdIOHandlerClass;
  708. AOwner: TComponent = nil): TIdIOHandler;
  709. begin
  710. Result := TryMakeIOHandler(ABaseType, AOwner);
  711. if not Assigned(Result) then begin
  712. raise EIdException.CreateFmt(RSIOHandlerTypeNotInstalled, [ABaseType.ClassName]); // TODO: create a new Exception class for this
  713. end;
  714. end;
  715. class function TIdIOHandler.TryMakeIOHandler(ABaseType: TIdIOHandlerClass;
  716. AOwner: TComponent = nil): TIdIOHandler;
  717. var
  718. i: Integer;
  719. begin
  720. if GIOHandlerClassList <> nil then begin
  721. for i := GIOHandlerClassList.Count - 1 downto 0 do begin
  722. if TIdIOHandlerClass(GIOHandlerClassList[i]).InheritsFrom(ABaseType) then begin
  723. Result := TIdIOHandlerClass(GIOHandlerClassList[i]).Create;
  724. Exit;
  725. end;
  726. end;
  727. end;
  728. Result := nil;
  729. end;
  730. function TIdIOHandler.GetDestination: string;
  731. begin
  732. Result := FDestination;
  733. end;
  734. procedure TIdIOHandler.SetDestination(const AValue: string);
  735. begin
  736. FDestination := AValue;
  737. end;
  738. procedure TIdIOHandler.BufferRemoveNotify(ASender: TObject; ABytes: Integer);
  739. begin
  740. DoWork(wmRead, ABytes);
  741. end;
  742. procedure TIdIOHandler.WriteBufferOpen(AThreshold: Integer);
  743. begin
  744. if FWriteBuffer <> nil then begin
  745. FWriteBuffer.Clear;
  746. end else begin
  747. FWriteBuffer := TIdBuffer.Create;
  748. end;
  749. FWriteBufferThreshold := AThreshold;
  750. end;
  751. procedure TIdIOHandler.WriteBufferClose;
  752. begin
  753. try
  754. WriteBufferFlush;
  755. finally
  756. FreeAndNil(FWriteBuffer);
  757. end;
  758. end;
  759. procedure TIdIOHandler.WriteBufferFlush(AByteCount: Integer);
  760. var
  761. LBytes: TIdBytes;
  762. begin
  763. if FWriteBuffer <> nil then begin
  764. if FWriteBuffer.Size > 0 then begin
  765. FWriteBuffer.ExtractToBytes(LBytes, AByteCount);
  766. WriteDirect(LBytes);
  767. end;
  768. end;
  769. end;
  770. procedure TIdIOHandler.WriteBufferClear;
  771. begin
  772. if FWriteBuffer <> nil then begin
  773. FWriteBuffer.Clear;
  774. end;
  775. end;
  776. procedure TIdIOHandler.WriteBufferCancel;
  777. begin
  778. WriteBufferClear;
  779. WriteBufferClose;
  780. end;
  781. procedure TIdIOHandler.Write(const AOut: string; AByteEncoding: IIdTextEncoding = nil);
  782. begin
  783. if AOut <> '' then begin
  784. AByteEncoding := iif(AByteEncoding, FDefStringEncoding);
  785. Write(ToBytes(AOut, -1, 1, AByteEncoding));
  786. end;
  787. end;
  788. procedure TIdIOHandler.Write(AValue: Byte);
  789. begin
  790. Write(ToBytes(AValue));
  791. end;
  792. procedure TIdIOHandler.Write(AValue: Char; AByteEncoding: IIdTextEncoding = nil);
  793. begin
  794. AByteEncoding := iif(AByteEncoding, FDefStringEncoding);
  795. Write(ToBytes(AValue, AByteEncoding));
  796. end;
  797. procedure TIdIOHandler.Write(AValue: UInt32; AConvert: Boolean = True);
  798. begin
  799. if AConvert then begin
  800. AValue := GStack.HostToNetwork(AValue);
  801. end;
  802. Write(ToBytes(AValue));
  803. end;
  804. procedure TIdIOHandler.Write(AValue: Int32; AConvert: Boolean = True);
  805. begin
  806. if AConvert then begin
  807. AValue := Int32(GStack.HostToNetwork(UInt32(AValue)));
  808. end;
  809. Write(ToBytes(AValue));
  810. end;
  811. procedure TIdIOHandler.Write(AValue: Int64; AConvert: Boolean = True);
  812. begin
  813. if AConvert then begin
  814. AValue := Int64(GStack.HostToNetwork(UInt64(AValue)));
  815. end;
  816. Write(ToBytes(AValue));
  817. end;
  818. procedure TIdIOHandler.Write(AValue: UInt64; AConvert: Boolean = True);
  819. begin
  820. if AConvert then begin
  821. AValue := GStack.HostToNetwork(AValue);
  822. end;
  823. Write(ToBytes(AValue));
  824. end;
  825. procedure TIdIOHandler.Write(AValue: TStrings; AWriteLinesCount: Boolean = False;
  826. AByteEncoding: IIdTextEncoding = nil);
  827. var
  828. i: Integer;
  829. LBufferingStarted: Boolean;
  830. begin
  831. AByteEncoding := iif(AByteEncoding, FDefStringEncoding);
  832. LBufferingStarted := not WriteBufferingActive;
  833. if LBufferingStarted then begin
  834. WriteBufferOpen;
  835. end;
  836. try
  837. if AWriteLinesCount then begin
  838. Write(AValue.Count);
  839. end;
  840. for i := 0 to AValue.Count - 1 do begin
  841. WriteLn(AValue.Strings[i], AByteEncoding);
  842. end;
  843. if LBufferingStarted then begin
  844. WriteBufferClose;
  845. end;
  846. except
  847. if LBufferingStarted then begin
  848. WriteBufferCancel;
  849. end;
  850. raise;
  851. end;
  852. end;
  853. procedure TIdIOHandler.Write(AValue: UInt16; AConvert: Boolean = True);
  854. begin
  855. if AConvert then begin
  856. AValue := GStack.HostToNetwork(AValue);
  857. end;
  858. Write(ToBytes(AValue));
  859. end;
  860. procedure TIdIOHandler.Write(AValue: Int16; AConvert: Boolean = True);
  861. begin
  862. if AConvert then begin
  863. AValue := Int16(GStack.HostToNetwork(UInt16(AValue)));
  864. end;
  865. Write(ToBytes(AValue));
  866. end;
  867. procedure TIdIOHandler.EnsureInputBytes(AByteCount: Integer);
  868. begin
  869. Assert(FInputBuffer<>nil);
  870. if AByteCount > 0 then begin
  871. // Read from stack until we have enough data
  872. while InputBuffer.Size < AByteCount do begin
  873. // RLebeau: in case the other party disconnects
  874. // after all of the bytes were transmitted ok.
  875. // No need to throw an exception just yet...
  876. if ReadFromSource(False) > 0 then begin
  877. if InputBuffer.Size >= AByteCount then begin
  878. Break; // we have enough data now
  879. end;
  880. end;
  881. CheckForDisconnect(True, True);
  882. end;
  883. end
  884. else if AByteCount < 0 then begin
  885. if InputBufferIsEmpty then begin
  886. // Read whatever data is currently on the stack
  887. ReadFromSource(False, ReadTimeout, False);
  888. CheckForDisconnect(True, True);
  889. end;
  890. end;
  891. end;
  892. function TIdIOHandler.ReadString(ABytes: Integer; AByteEncoding: IIdTextEncoding = nil): string;
  893. var
  894. LBytes: TIdBytes;
  895. begin
  896. if ABytes > 0 then begin
  897. EnsureInputBytes(ABytes);
  898. AByteEncoding := iif(AByteEncoding, FDefStringEncoding);
  899. Result := InputBuffer.ExtractToString(ABytes, AByteEncoding);
  900. end else begin
  901. Result := '';
  902. end;
  903. end;
  904. procedure TIdIOHandler.ReadStrings(ADest: TStrings; AReadLinesCount: Integer = -1;
  905. AByteEncoding: IIdTextEncoding = nil);
  906. var
  907. i: Integer;
  908. begin
  909. AByteEncoding := iif(AByteEncoding, FDefStringEncoding);
  910. if AReadLinesCount < 0 then begin
  911. AReadLinesCount := ReadInt32;
  912. end;
  913. ADest.BeginUpdate;
  914. try
  915. for i := 0 to AReadLinesCount - 1 do begin
  916. ADest.Add(ReadLn(AByteEncoding));
  917. end;
  918. finally
  919. ADest.EndUpdate;
  920. end;
  921. end;
  922. function TIdIOHandler.ReadUInt16(AConvert: Boolean = True): UInt16;
  923. begin
  924. EnsureInputBytes(SizeOf(UInt16));
  925. Result := InputBuffer.ExtractToUInt16(-1, AConvert);
  926. end;
  927. function TIdIOHandler.ReadInt16(AConvert: Boolean = True): Int16;
  928. begin
  929. EnsureInputBytes(SizeOf(Int16));
  930. Result := Int16(InputBuffer.ExtractToUInt16(-1, AConvert));
  931. end;
  932. function TIdIOHandler.ReadChar(AByteEncoding: IIdTextEncoding = nil): Char;
  933. var
  934. I, J, NumChars, NumBytes: Integer;
  935. LBytes: TIdBytes;
  936. LChars: TIdWideChars;
  937. begin
  938. AByteEncoding := iif(AByteEncoding, FDefStringEncoding);
  939. // 2 Chars to handle UTF-16 surrogates
  940. NumBytes := AByteEncoding.GetMaxByteCount(2);
  941. SetLength(LBytes, NumBytes);
  942. SetLength(LChars, 2);
  943. NumChars := 0;
  944. if NumBytes > 0 then
  945. begin
  946. for I := 1 to NumBytes do
  947. begin
  948. LBytes[I-1] := ReadByte;
  949. NumChars := AByteEncoding.GetChars(LBytes, 0, I, LChars, 0);
  950. if NumChars > 0 then begin
  951. // RLebeau 10/19/2012: when Indy switched to its own UTF-8 implementation
  952. // to avoid the MB_ERR_INVALID_CHARS flag on Windows, it accidentally broke
  953. // this loop! Since this is not commonly used, this was not noticed until
  954. // now. On Windows at least, GetChars() now returns >0 for an invalid
  955. // sequence, so we have to check if any of the returned characters are the
  956. // Unicode U+FFFD character, indicating bad data...
  957. for J := 0 to NumChars-1 do begin
  958. if LChars[J] = WideChar($FFFD) then begin
  959. // keep reading...
  960. NumChars := 0;
  961. Break;
  962. end;
  963. end;
  964. if NumChars > 0 then begin
  965. Break;
  966. end;
  967. end;
  968. end;
  969. end;
  970. // RLebeau: if the bytes were decoded into surrogates, the second
  971. // surrogate is lost here, as it can't be returned unless we cache
  972. // it somewhere for the the next ReadChar() call to retreive. Just
  973. // raise an error for now. Users will have to update their code to
  974. // read surrogates differently...
  975. Assert(NumChars = 1);
  976. Result := LChars[0];
  977. end;
  978. function TIdIOHandler.ReadByte: Byte;
  979. begin
  980. EnsureInputBytes(SizeOf(Byte));
  981. Result := InputBuffer.ExtractToUInt8(-1);
  982. end;
  983. function TIdIOHandler.ReadInt32(AConvert: Boolean): Int32;
  984. begin
  985. EnsureInputBytes(SizeOf(Int32));
  986. Result := Int32(InputBuffer.ExtractToUInt32(-1, AConvert));
  987. end;
  988. function TIdIOHandler.ReadInt64(AConvert: boolean): Int64;
  989. begin
  990. EnsureInputBytes(SizeOf(Int64));
  991. Result := Int64(InputBuffer.ExtractToUInt64(-1, AConvert));
  992. end;
  993. function TIdIOHandler.ReadUInt64(AConvert: boolean): UInt64;
  994. begin
  995. EnsureInputBytes(SizeOf(UInt64));
  996. Result := InputBuffer.ExtractToUInt64(-1, AConvert);
  997. end;
  998. function TIdIOHandler.ReadUInt32(AConvert: Boolean): UInt32;
  999. begin
  1000. EnsureInputBytes(SizeOf(UInt32));
  1001. Result := InputBuffer.ExtractToUInt32(-1, AConvert);
  1002. end;
  1003. function TIdIOHandler.ReadLn(AByteEncoding: IIdTextEncoding = nil): string;
  1004. {$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
  1005. begin
  1006. Result := ReadLn(LF, IdTimeoutDefault, -1, AByteEncoding);
  1007. end;
  1008. function TIdIOHandler.ReadLn(ATerminator: string; AByteEncoding: IIdTextEncoding): string;
  1009. {$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
  1010. begin
  1011. Result := ReadLn(ATerminator, IdTimeoutDefault, -1, AByteEncoding);
  1012. end;
  1013. function TIdIOHandler.ReadLn(ATerminator: string; ATimeout: Integer = IdTimeoutDefault;
  1014. AMaxLineLength: Integer = -1; AByteEncoding: IIdTextEncoding = nil): string;
  1015. var
  1016. LInputBufferSize: Integer;
  1017. LStartPos: Integer;
  1018. LTermPos: Integer;
  1019. LReadLnStartTime: TIdTicks;
  1020. LTerm, LResult: TIdBytes;
  1021. begin
  1022. AByteEncoding := iif(AByteEncoding, FDefStringEncoding);
  1023. if AMaxLineLength < 0 then begin
  1024. AMaxLineLength := MaxLineLength;
  1025. end;
  1026. // User may pass '' if they need to pass arguments beyond the first.
  1027. if ATerminator = '' then begin
  1028. ATerminator := LF;
  1029. end;
  1030. // TODO: encountered an email that was using charset "cp1026", which encodes
  1031. // a LF character to byte $25 instead of $0A (and decodes byte $0A to character
  1032. // #$8E instead of #$A). To account for that, don't encoding the LF using the
  1033. // specified encoding anymore, force the encoding to what it should be. But
  1034. // what if UTF-16 is being used?
  1035. {
  1036. if ATerminator = LF then begin
  1037. LTerm := ToBytes(Byte($0A));
  1038. end else begin
  1039. LTerm := ToBytes(ATerminator, AByteEncoding);
  1040. end;
  1041. }
  1042. LTerm := ToBytes(ATerminator, AByteEncoding);
  1043. FReadLnSplit := False;
  1044. FReadLnTimedOut := False;
  1045. LTermPos := -1;
  1046. LStartPos := 0;
  1047. LReadLnStartTime := Ticks64;
  1048. repeat
  1049. LInputBufferSize := FInputBuffer.Size;
  1050. if LInputBufferSize > 0 then begin
  1051. if LStartPos < LInputBufferSize then begin
  1052. LTermPos := FInputBuffer.IndexOf(LTerm, LStartPos);
  1053. end else begin
  1054. LTermPos := -1;
  1055. end;
  1056. LStartPos := IndyMax(LInputBufferSize-(Length(LTerm)-1), 0);
  1057. end;
  1058. // if the line length is limited and terminator is found after the limit or not found and the limit is exceeded
  1059. if (AMaxLineLength > 0) and ((LTermPos > AMaxLineLength) or ((LTermPos = -1) and (LStartPos > AMaxLineLength))) then begin
  1060. if MaxLineAction = maException then begin
  1061. raise EIdReadLnMaxLineLengthExceeded.Create(RSReadLnMaxLineLengthExceeded);
  1062. end;
  1063. // RLebeau: WARNING - if the line is using multibyte character sequences
  1064. // and a sequence staddles the AMaxLineLength boundary, this will chop
  1065. // the sequence, producing invalid data!
  1066. FReadLnSplit := True;
  1067. Result := FInputBuffer.ExtractToString(AMaxLineLength, AByteEncoding);
  1068. Exit;
  1069. end
  1070. // ReadFromSource blocks - do not call unless we need to
  1071. else if LTermPos = -1 then begin
  1072. // ReadLn needs to call this as data may exist in the buffer, but no EOL yet disconnected
  1073. CheckForDisconnect(True, True);
  1074. // Can only return -1 if timeout
  1075. FReadLnTimedOut := ReadFromSource(True, ATimeout, False) = -1;
  1076. if (not FReadLnTimedOut) and (ATimeout >= 0) then begin
  1077. if GetElapsedTicks(LReadLnStartTime) >= UInt32(ATimeout) then begin
  1078. FReadLnTimedOut := True;
  1079. end;
  1080. end;
  1081. if FReadLnTimedOut then begin
  1082. Result := '';
  1083. Exit;
  1084. end;
  1085. end;
  1086. until LTermPos > -1;
  1087. // Extract actual data
  1088. {
  1089. IMPORTANT!!!
  1090. When encoding from UTF8 to Unicode or ASCII, you will not always get the same
  1091. number of bytes that you input so you may have to recalculate LTermPos since
  1092. that was based on the number of bytes in the input stream. If do not do this,
  1093. you will probably get an incorrect result or a range check error since the
  1094. string is shorter then the original buffer position.
  1095. JPM
  1096. }
  1097. // RLebeau 11/19/08: this is no longer needed as the terminator is encoded to raw bytes now ...
  1098. {
  1099. Result := FInputBuffer.Extract(LTermPos + Length(ATerminator), AEncoding);
  1100. LTermPos := IndyMin(LTermPos, Length(Result));
  1101. if (ATerminator = LF) and (LTermPos > 0) then begin
  1102. if Result[LTermPos] = CR then begin
  1103. Dec(LTermPos);
  1104. end;
  1105. end;
  1106. SetLength(Result, LTermPos);
  1107. }
  1108. FInputBuffer.ExtractToBytes(LResult, LTermPos + Length(LTerm));
  1109. if (ATerminator = LF) and (LTermPos > 0) then begin
  1110. if LResult[LTermPos-1] = Ord(CR) then begin
  1111. Dec(LTermPos);
  1112. end;
  1113. end;
  1114. Result := BytesToString(LResult, 0, LTermPos, AByteEncoding);
  1115. end;
  1116. function TIdIOHandler.ReadLnRFC(var VMsgEnd: Boolean; AByteEncoding: IIdTextEncoding = nil): string;
  1117. {$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
  1118. begin
  1119. Result := ReadLnRFC(VMsgEnd, LF, '.', AByteEncoding); {do not localize}
  1120. end;
  1121. function TIdIOHandler.ReadLnRFC(var VMsgEnd: Boolean; const ALineTerminator: string;
  1122. const ADelim: String = '.'; AByteEncoding: IIdTextEncoding = nil): string;
  1123. begin
  1124. Result := ReadLn(ALineTerminator, AByteEncoding);
  1125. // Do not use ATerminator since always ends with . (standard)
  1126. if Result = ADelim then
  1127. begin
  1128. VMsgEnd := True;
  1129. Exit;
  1130. end;
  1131. if TextStartsWith(Result, '..') then begin {do not localize}
  1132. Delete(Result, 1, 1);
  1133. end;
  1134. VMsgEnd := False;
  1135. end;
  1136. function TIdIOHandler.ReadLnSplit(var AWasSplit: Boolean; ATerminator: string = LF;
  1137. ATimeout: Integer = IdTimeoutDefault; AMaxLineLength: Integer = -1;
  1138. AByteEncoding: IIdTextEncoding = nil): string;
  1139. var
  1140. FOldAction: TIdMaxLineAction;
  1141. begin
  1142. FOldAction := MaxLineAction;
  1143. MaxLineAction := maSplit;
  1144. try
  1145. Result := ReadLn(ATerminator, ATimeout, AMaxLineLength, AByteEncoding);
  1146. AWasSplit := FReadLnSplit;
  1147. finally
  1148. MaxLineAction := FOldAction;
  1149. end;
  1150. end;
  1151. function TIdIOHandler.ReadLnWait(AFailCount: Integer = MaxInt; AByteEncoding: IIdTextEncoding = nil): string;
  1152. var
  1153. LAttempts: Integer;
  1154. begin
  1155. // MtW: this is mostly used when empty lines could be sent.
  1156. AByteEncoding := iif(AByteEncoding, FDefStringEncoding);
  1157. Result := '';
  1158. LAttempts := 0;
  1159. while LAttempts < AFailCount do
  1160. begin
  1161. Result := Trim(ReadLn(AByteEncoding));
  1162. if Result <> '' then begin
  1163. Exit;
  1164. end;
  1165. if ReadLnTimedOut then begin
  1166. raise EIdReadTimeout.Create(RSReadTimeout);
  1167. end;
  1168. Inc(LAttempts);
  1169. end;
  1170. raise EIdReadLnWaitMaxAttemptsExceeded.Create(RSReadLnWaitMaxAttemptsExceeded);
  1171. end;
  1172. function TIdIOHandler.ReadFromSource(ARaiseExceptionIfDisconnected: Boolean;
  1173. ATimeout: Integer; ARaiseExceptionOnTimeout: Boolean): Integer;
  1174. var
  1175. LByteCount: Integer;
  1176. LLastError: Integer;
  1177. LBuffer: TIdBytes;
  1178. // under ARC, convert a weak reference to a strong reference before working with it
  1179. LIntercept: TIdConnectionIntercept;
  1180. begin
  1181. if ATimeout = IdTimeoutDefault then begin
  1182. // MtW: check for 0 too, for compatibility
  1183. if (ReadTimeout = IdTimeoutDefault) or (ReadTimeout = 0) then begin
  1184. ATimeout := IdTimeoutInfinite;
  1185. end else begin
  1186. ATimeout := ReadTimeout;
  1187. end;
  1188. end;
  1189. Result := 0;
  1190. // Check here as this side may have closed the socket
  1191. CheckForDisconnect(ARaiseExceptionIfDisconnected);
  1192. if SourceIsAvailable then begin
  1193. repeat
  1194. LByteCount := 0;
  1195. // TODO: move the handling of Readable() into ReadDataFromSource() of descendant
  1196. // classes. For a plain socket, it makes sense to check for readability before
  1197. // attempting to read. Or just perform a blocking read w/ timeout applied. But for
  1198. // OpenSSL, you are not really supposed to check for readability first! You are
  1199. // supposed to attempt to read first, and then wait for new data w/ timeout only
  1200. // if OpenSSL explicitly asks you to do so after the read fails...
  1201. if Readable(ATimeout) then begin
  1202. if Opened then begin
  1203. // No need to call AntiFreeze, the Readable does that.
  1204. if SourceIsAvailable then begin
  1205. // TODO: Whey are we reallocating LBuffer every time? This
  1206. // should be a one time operation per connection.
  1207. // RLebeau: because the Intercept does not allow the buffer
  1208. // size to be specified, and the Intercept could potentially
  1209. // resize the buffer...
  1210. SetLength(LBuffer, RecvBufferSize);
  1211. try
  1212. LByteCount := ReadDataFromSource(LBuffer);
  1213. if LByteCount > 0 then begin
  1214. SetLength(LBuffer, LByteCount);
  1215. LIntercept := Intercept;
  1216. if LIntercept <> nil then begin
  1217. LIntercept.Receive(LBuffer);
  1218. {$IFDEF USE_OBJECT_ARC}LIntercept := nil;{$ENDIF}
  1219. LByteCount := Length(LBuffer);
  1220. end;
  1221. // Pass through LBuffer first so it can go through Intercept
  1222. //TODO: If not intercept, we can skip this step
  1223. InputBuffer.Write(LBuffer);
  1224. end;
  1225. finally
  1226. LBuffer := nil;
  1227. end;
  1228. end
  1229. else if ARaiseExceptionIfDisconnected then begin
  1230. raise EIdClosedSocket.Create(RSStatusDisconnected);
  1231. end;
  1232. end
  1233. else if ARaiseExceptionIfDisconnected then begin
  1234. raise EIdNotConnected.Create(RSNotConnected);
  1235. end;
  1236. if LByteCount < 0 then
  1237. begin
  1238. LLastError := CheckForError(LByteCount);
  1239. if LLastError = Id_WSAETIMEDOUT then begin
  1240. // Timeout
  1241. if ARaiseExceptionOnTimeout then begin
  1242. raise EIdReadTimeout.Create(RSReadTimeout);
  1243. end;
  1244. Result := -1;
  1245. Break;
  1246. end;
  1247. FClosedGracefully := True;
  1248. Close;
  1249. // Do not raise unless all data has been read by the user
  1250. if InputBufferIsEmpty and ARaiseExceptionIfDisconnected then begin
  1251. RaiseError(LLastError);
  1252. end;
  1253. LByteCount := 0;
  1254. end
  1255. else if LByteCount = 0 then begin
  1256. FClosedGracefully := True;
  1257. end;
  1258. // Check here as other side may have closed connection
  1259. CheckForDisconnect(ARaiseExceptionIfDisconnected);
  1260. Result := LByteCount;
  1261. end else begin
  1262. // Timeout
  1263. if ARaiseExceptionOnTimeout then begin
  1264. raise EIdReadTimeout.Create(RSReadTimeout);
  1265. end;
  1266. Result := -1;
  1267. Break;
  1268. end;
  1269. until (LByteCount <> 0) or (not SourceIsAvailable);
  1270. end
  1271. else if ARaiseExceptionIfDisconnected then begin
  1272. raise EIdNotConnected.Create(RSNotConnected);
  1273. end;
  1274. end;
  1275. function TIdIOHandler.CheckForDataOnSource(ATimeout: Integer = 0): Boolean;
  1276. var
  1277. LPrevSize: Integer;
  1278. begin
  1279. Result := False;
  1280. // RLebeau - Connected() might read data into the InputBuffer, thus
  1281. // leaving no data for ReadFromSource() to receive a second time,
  1282. // causing a result of False when it should be True instead. So we
  1283. // save the current size of the InputBuffer before calling Connected()
  1284. // and then compare it afterwards....
  1285. LPrevSize := InputBuffer.Size;
  1286. if Connected then begin
  1287. // return whether at least 1 byte was received
  1288. Result := (InputBuffer.Size > LPrevSize) or (ReadFromSource(False, ATimeout, False) > 0);
  1289. end;
  1290. // TODO: since Connected() just calls ReadFromSource() anyway, maybe just
  1291. // call ReadFromSource() by itself and not call Connected() at all?
  1292. // Result := ReadFromSource(False, ATimeout, False) > 0;
  1293. end;
  1294. procedure TIdIOHandler.Write(AStream: TStream; ASize: Int64 = 0;
  1295. AWriteByteCount: Boolean = FALSE);
  1296. var
  1297. LBuffer: TIdBytes;
  1298. LStreamPos: Int64;
  1299. LBufSize: Integer;
  1300. // LBufferingStarted: Boolean;
  1301. begin
  1302. // TODO: when AWriteByteCount is false, don't calculate the Size, just keep
  1303. // sending data until the end of stream is reached. This way, we can send
  1304. // streams that are not able to report accurate Size values...
  1305. if ASize < 0 then begin //"-1" All from current position
  1306. LStreamPos := AStream.Position;
  1307. ASize := AStream.Size - LStreamPos;
  1308. //todo is this step required?
  1309. AStream.Position := LStreamPos;
  1310. end
  1311. else if ASize = 0 then begin //"0" ALL
  1312. ASize := AStream.Size;
  1313. AStream.Position := 0;
  1314. end;
  1315. //else ">0" number of bytes
  1316. // RLebeau 3/19/2006: DO NOT ENABLE WRITE BUFFERING IN THIS METHOD!
  1317. //
  1318. // When sending large streams, especially with LargeStream enabled,
  1319. // this can easily cause "Out of Memory" errors. It is the caller's
  1320. // responsibility to enable/disable write buffering as needed before
  1321. // calling one of the Write() methods.
  1322. //
  1323. // Also, forcing write buffering in this method is having major
  1324. // impacts on TIdFTP, TIdFTPServer, and TIdHTTPServer.
  1325. if AWriteByteCount then begin
  1326. if LargeStream then begin
  1327. Write(ASize);
  1328. end else begin
  1329. if ASize > High(Integer) then begin
  1330. raise EIdIOHandlerRequiresLargeStream.Create(RSRequiresLargeStream);
  1331. end;
  1332. Write(Int32(ASize));
  1333. end;
  1334. end;
  1335. BeginWork(wmWrite, ASize);
  1336. try
  1337. SetLength(LBuffer, FSendBufferSize);
  1338. while ASize > 0 do begin
  1339. LBufSize := IndyMin(ASize, Length(LBuffer));
  1340. // Do not use ReadBuffer. Some source streams are real time and will not
  1341. // return as much data as we request. Kind of like recv()
  1342. // NOTE: We use .Size - size must be supported even if real time
  1343. LBufSize := AStream.Read(PByte(LBuffer)^, LBufSize);
  1344. if LBufSize <= 0 then begin
  1345. raise EIdNoDataToRead.Create(RSIdNoDataToRead);
  1346. end;
  1347. Write(LBuffer, LBufSize);
  1348. // RLebeau: DoWork() is called in WriteDirect()
  1349. //DoWork(wmWrite, LBufSize);
  1350. Dec(ASize, LBufSize);
  1351. end;
  1352. finally
  1353. EndWork(wmWrite);
  1354. LBuffer := nil;
  1355. end;
  1356. end;
  1357. procedure TIdIOHandler.ReadBytes(var VBuffer: TIdBytes; AByteCount: Integer; AAppend: Boolean = True);
  1358. begin
  1359. EnsureInputBytes(AByteCount);
  1360. InputBuffer.ExtractToBytes(VBuffer, AByteCount, AAppend);
  1361. end;
  1362. procedure TIdIOHandler.WriteLn(AEncoding: IIdTextEncoding = nil);
  1363. {$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
  1364. begin
  1365. WriteLn('', AEncoding);
  1366. end;
  1367. procedure TIdIOHandler.WriteLn(const AOut: string; AByteEncoding: IIdTextEncoding = nil);
  1368. begin
  1369. // TODO: RLebeau 1/2/2015: encountered an email that was using charset "cp1026",
  1370. // which encodes a LF character to byte $25 instead of $0A (and decodes
  1371. // byte $0A to character #$8E instead of #$A). To account for that, don't
  1372. // encoding the CRLF using the specified encoding anymore, force the encoding
  1373. // to what it should be...
  1374. //
  1375. // But, what to do if the target encoding is UTF-16?
  1376. {
  1377. LBufferingStarted := not WriteBufferingActive;
  1378. if LBufferingStarted then begin
  1379. WriteBufferOpen;
  1380. end;
  1381. try
  1382. Write(AOut, AByteEncoding);
  1383. Write(EOL, Indy8BitEncoding);
  1384. if LBufferingStarted then begin
  1385. WriteBufferClose;
  1386. end;
  1387. except
  1388. if LBufferingStarted then begin
  1389. WriteBufferCancel;
  1390. end;
  1391. raise;
  1392. end;
  1393. }
  1394. // Do as one write so it only makes one call to network
  1395. Write(AOut + EOL, AByteEncoding);
  1396. end;
  1397. procedure TIdIOHandler.WriteLnRFC(const AOut: string = '';
  1398. AByteEncoding: IIdTextEncoding = nil);
  1399. begin
  1400. if TextStartsWith(AOut, '.') then begin {do not localize}
  1401. WriteLn('.' + AOut, AByteEncoding); {do not localize}
  1402. end else begin
  1403. WriteLn(AOut, AByteEncoding);
  1404. end;
  1405. end;
  1406. function TIdIOHandler.Readable(AMSec: Integer): Boolean;
  1407. begin
  1408. // In case descendant does not override this or other methods but implements the higher level
  1409. // methods
  1410. Result := False;
  1411. end;
  1412. procedure TIdIOHandler.SetHost(const AValue: string);
  1413. begin
  1414. FHost := AValue;
  1415. end;
  1416. procedure TIdIOHandler.SetPort(AValue: Integer);
  1417. begin
  1418. FPort := AValue;
  1419. end;
  1420. function TIdIOHandler.Connected: Boolean;
  1421. begin
  1422. CheckForDisconnect(False);
  1423. Result :=
  1424. (
  1425. (
  1426. // Set when closed properly. Reflects actual socket state.
  1427. (not ClosedGracefully)
  1428. // Created on Open. Prior to Open ClosedGracefully is still false.
  1429. and (FInputBuffer <> nil)
  1430. )
  1431. // Buffer must be empty. Even if closed, we are "connected" if we still have
  1432. // data
  1433. or (not InputBufferIsEmpty)
  1434. )
  1435. and Opened;
  1436. end;
  1437. // TODO: move this into IdGlobal.pas
  1438. procedure AdjustStreamSize(const AStream: TStream; const ASize: Int64);
  1439. var
  1440. LStreamPos: Int64;
  1441. begin
  1442. LStreamPos := AStream.Position;
  1443. AStream.Size := ASize;
  1444. // Must reset to original value in cases where size changes position
  1445. if AStream.Position <> LStreamPos then begin
  1446. AStream.Position := LStreamPos;
  1447. end;
  1448. end;
  1449. procedure TIdIOHandler.ReadStream(AStream: TStream; AByteCount: Int64;
  1450. AReadUntilDisconnect: Boolean);
  1451. var
  1452. LByteCount, LPos: Int64;
  1453. procedure CheckInputBufferForData;
  1454. var
  1455. i: Integer;
  1456. begin
  1457. i := FInputBuffer.Size;
  1458. if i > 0 then begin
  1459. if not AReadUntilDisconnect then begin
  1460. i := Integer(IndyMin(Int64(i), LByteCount));
  1461. Dec(LByteCount, i);
  1462. end;
  1463. if AStream <> nil then begin
  1464. FInputBuffer.ExtractToStream(AStream, i);
  1465. end else begin
  1466. FInputBuffer.Remove(i);
  1467. end;
  1468. end;
  1469. end;
  1470. const
  1471. cSizeUnknown = -1;
  1472. begin
  1473. if (AByteCount = cSizeUnknown) and (not AReadUntilDisconnect) then begin
  1474. // Read size from connection
  1475. if LargeStream then begin
  1476. LByteCount := ReadInt64;
  1477. end else begin
  1478. LByteCount := ReadInt32;
  1479. end;
  1480. end else begin
  1481. LByteCount := AByteCount;
  1482. end;
  1483. // Presize stream if we know the size - this reduces memory/disk allocations to one time.
  1484. // TODO: need to add an option for this. user might not want to presize here, eg for reading
  1485. // int64 files, or when filling a manually-sized file using multiple threads.
  1486. if (AStream <> nil) and (LByteCount > -1) then begin
  1487. LPos := AStream.Position;
  1488. if (High(Int64) - LPos) < LByteCount then begin
  1489. raise EIdIOHandlerStreamDataTooLarge.Create(RSDataTooLarge);
  1490. end;
  1491. AdjustStreamSize(AStream, LPos + LByteCount);
  1492. end;
  1493. if (LByteCount <= cSizeUnknown) and (not AReadUntilDisconnect) then begin
  1494. AReadUntilDisconnect := True;
  1495. end;
  1496. if AReadUntilDisconnect then begin
  1497. BeginWork(wmRead);
  1498. end else begin
  1499. BeginWork(wmRead, LByteCount);
  1500. end;
  1501. try
  1502. // If data already exists in the buffer, write it out first.
  1503. CheckInputBufferForData;
  1504. // RLebeau - don't call Connected() here! It can cause an
  1505. // EIdConnClosedGracefully exception that breaks the loop
  1506. // prematurely and thus leave unread bytes in the InputBuffer.
  1507. // Let the loop handle disconnects before exiting...
  1508. // RLebeau 5/21/2019: rewritting this method to no longer use
  1509. // ReadBytes(), to avoid side-effects of an FMX issue with catching
  1510. // and reraising exceptions here during TIdFTP data transfers on iOS,
  1511. // per this blog:
  1512. //
  1513. // https://www.delphiworlds.com/2013/10/fixing-tidftp-for-ios-devices/
  1514. //
  1515. // Besides, using ReadBytes() with exception handling here was always
  1516. // an ugly hack that we wanted to get rid of anyway, now its gone...
  1517. while AReadUntilDisconnect or (LByteCount > 0) do begin
  1518. try
  1519. // Read from stack to get more data
  1520. if ReadFromSource(not AReadUntilDisconnect) < 1 then begin
  1521. CheckForDisconnect(False);
  1522. Break;
  1523. end;
  1524. TIdAntiFreezeBase.DoProcess;
  1525. finally
  1526. CheckInputBufferForData;
  1527. end;
  1528. end;
  1529. finally
  1530. EndWork(wmRead);
  1531. if AStream <> nil then begin
  1532. if AStream.Size > AStream.Position then begin
  1533. AStream.Size := AStream.Position;
  1534. end;
  1535. end;
  1536. end;
  1537. end;
  1538. procedure TIdIOHandler.Discard(AByteCount: Int64);
  1539. var
  1540. LSize: Integer;
  1541. begin
  1542. Assert(AByteCount >= 0);
  1543. if AByteCount > 0 then
  1544. begin
  1545. BeginWork(wmRead, AByteCount);
  1546. try
  1547. repeat
  1548. if AByteCount < MaxInt then begin
  1549. LSize := Integer(AByteCount);
  1550. end else begin
  1551. LSize := MaxInt;
  1552. end;
  1553. LSize := IndyMin(LSize, FInputBuffer.Size);
  1554. if LSize > 0 then begin
  1555. FInputBuffer.Remove(LSize);
  1556. Dec(AByteCount, LSize);
  1557. if AByteCount < 1 then begin
  1558. Break;
  1559. end;
  1560. end;
  1561. // RLebeau: in case the other party disconnects
  1562. // after all of the bytes were transmitted ok.
  1563. // No need to throw an exception just yet...
  1564. if ReadFromSource(False) < 1 then begin
  1565. CheckForDisconnect(True, True);
  1566. end;
  1567. until False;
  1568. finally
  1569. EndWork(wmRead);
  1570. end;
  1571. end;
  1572. end;
  1573. procedure TIdIOHandler.DiscardAll;
  1574. begin
  1575. BeginWork(wmRead);
  1576. try
  1577. // If data already exists in the buffer, discard it first.
  1578. FInputBuffer.Clear;
  1579. // RLebeau - don't call Connected() here! ReadBytes() already
  1580. // does that internally. Calling Connected() here can cause an
  1581. // EIdConnClosedGracefully exception that breaks the loop
  1582. // prematurely and thus leave unread bytes in the InputBuffer.
  1583. // Let the loop catch the exception before exiting...
  1584. repeat
  1585. //TODO: Improve this - dont like the use of the exception handler
  1586. try
  1587. if ReadFromSource(False) > 0 then begin
  1588. FInputBuffer.Clear;
  1589. end else begin;
  1590. CheckForDisconnect(True, True);
  1591. end;
  1592. except
  1593. on E: Exception do begin
  1594. // RLebeau - ReadFromSource() could have filled the
  1595. // InputBuffer with more bytes...
  1596. FInputBuffer.Clear;
  1597. if E is EIdConnClosedGracefully then begin
  1598. Break;
  1599. end else begin
  1600. raise;
  1601. end;
  1602. end;
  1603. end;
  1604. TIdAntiFreezeBase.DoProcess;
  1605. until False;
  1606. finally
  1607. EndWork(wmRead);
  1608. end;
  1609. end;
  1610. procedure TIdIOHandler.RaiseConnClosedGracefully;
  1611. begin
  1612. (* ************************************************************* //
  1613. ------ If you receive an exception here, please read. ----------
  1614. If this is a SERVER
  1615. -------------------
  1616. The client has disconnected the socket normally and this exception is used to notify the
  1617. server handling code. This exception is normal and will only happen from within the IDE, not
  1618. while your program is running as an EXE. If you do not want to see this, add this exception
  1619. or EIdSilentException to the IDE options as exceptions not to break on.
  1620. From the IDE just hit F9 again and Indy will catch and handle the exception.
  1621. Please see the FAQ and help file for possible further information.
  1622. The FAQ is at http://www.nevrona.com/Indy/FAQ.html
  1623. If this is a CLIENT
  1624. -------------------
  1625. The server side of this connection has disconnected normaly but your client has attempted
  1626. to read or write to the connection. You should trap this error using a try..except.
  1627. Please see the help file for possible further information.
  1628. // ************************************************************* *)
  1629. raise EIdConnClosedGracefully.Create(RSConnectionClosedGracefully);
  1630. end;
  1631. function TIdIOHandler.InputBufferAsString(AByteEncoding: IIdTextEncoding = nil): string;
  1632. begin
  1633. AByteEncoding := iif(AByteEncoding, FDefStringEncoding);
  1634. Result := FInputBuffer.ExtractToString(FInputBuffer.Size, AByteEncoding);
  1635. end;
  1636. function TIdIOHandler.AllData(AByteEncoding: IIdTextEncoding = nil): string;
  1637. var
  1638. LBytes: Integer;
  1639. begin
  1640. Result := '';
  1641. BeginWork(wmRead);
  1642. try
  1643. if Connected then
  1644. begin
  1645. try
  1646. try
  1647. repeat
  1648. LBytes := ReadFromSource(False, 250, False);
  1649. until LBytes = 0; // -1 on timeout
  1650. finally
  1651. if not InputBufferIsEmpty then begin
  1652. Result := InputBufferAsString(AByteEncoding);
  1653. end;
  1654. end;
  1655. except end;
  1656. end;
  1657. finally
  1658. EndWork(wmRead);
  1659. end;
  1660. end;
  1661. procedure TIdIOHandler.PerformCapture(const ADest: TObject;
  1662. out VLineCount: Integer; const ADelim: string;
  1663. AUsesDotTransparency: Boolean; AByteEncoding: IIdTextEncoding = nil);
  1664. var
  1665. s: string;
  1666. LStream: TStream;
  1667. LStrings: TStrings;
  1668. begin
  1669. VLineCount := 0;
  1670. AByteEncoding := iif(AByteEncoding, FDefStringEncoding);
  1671. LStream := nil;
  1672. LStrings := nil;
  1673. if ADest is TStrings then begin
  1674. LStrings := TStrings(ADest);
  1675. end
  1676. else if ADest is TStream then begin
  1677. LStream := TStream(ADest);
  1678. end
  1679. else begin
  1680. raise EIdObjectTypeNotSupported.Create(RSObjectTypeNotSupported);
  1681. end;
  1682. BeginWork(wmRead);
  1683. try
  1684. if LStrings <> nil then begin
  1685. LStrings.BeginUpdate;
  1686. end;
  1687. try
  1688. repeat
  1689. s := ReadLn(AByteEncoding);
  1690. if s = ADelim then begin
  1691. Exit;
  1692. end;
  1693. // S.G. 6/4/2004: All the consumers to protect themselves against memory allocation attacks
  1694. if FMaxCapturedLines > 0 then begin
  1695. if VLineCount > FMaxCapturedLines then begin
  1696. raise EIdMaxCaptureLineExceeded.Create(RSMaximumNumberOfCaptureLineExceeded);
  1697. end;
  1698. end;
  1699. // For RFC retrieves that use dot transparency
  1700. // No length check necessary, if only one byte it will be byte x + #0.
  1701. if AUsesDotTransparency then begin
  1702. if TextStartsWith(s, '..') then begin
  1703. Delete(s, 1, 1);
  1704. end;
  1705. end;
  1706. // Write to output
  1707. Inc(VLineCount);
  1708. if LStrings <> nil then begin
  1709. LStrings.Add(s);
  1710. end
  1711. else if LStream <> nil then begin
  1712. WriteStringToStream(LStream, s+EOL, AByteEncoding);
  1713. end;
  1714. until False;
  1715. finally
  1716. if LStrings <> nil then begin
  1717. LStrings.EndUpdate;
  1718. end;
  1719. end;
  1720. finally
  1721. EndWork(wmRead);
  1722. end;
  1723. end;
  1724. function TIdIOHandler.InputLn(const AMask: String = ''; AEcho: Boolean = True;
  1725. ATabWidth: Integer = 8; AMaxLineLength: Integer = -1;
  1726. AByteEncoding: IIdTextEncoding = nil): String;
  1727. var
  1728. i: Integer;
  1729. LChar: Char;
  1730. LTmp: string;
  1731. begin
  1732. Result := '';
  1733. AByteEncoding := iif(AByteEncoding, FDefStringEncoding);
  1734. if AMaxLineLength < 0 then begin
  1735. AMaxLineLength := MaxLineLength;
  1736. end;
  1737. repeat
  1738. LChar := ReadChar(AByteEncoding);
  1739. i := Length(Result);
  1740. if i <= AMaxLineLength then begin
  1741. case LChar of
  1742. BACKSPACE:
  1743. begin
  1744. if i > 0 then begin
  1745. SetLength(Result, i - 1);
  1746. if AEcho then begin
  1747. Write(BACKSPACE + ' ' + BACKSPACE, AByteEncoding);
  1748. end;
  1749. end;
  1750. end;
  1751. TAB:
  1752. begin
  1753. if ATabWidth > 0 then begin
  1754. i := ATabWidth - (i mod ATabWidth);
  1755. LTmp := StringOfChar(' ', i);
  1756. Result := Result + LTmp;
  1757. if AEcho then begin
  1758. Write(LTmp, AByteEncoding);
  1759. end;
  1760. end else begin
  1761. Result := Result + LChar;
  1762. if AEcho then begin
  1763. Write(LChar, AByteEncoding);
  1764. end;
  1765. end;
  1766. end;
  1767. LF: ;
  1768. CR: ;
  1769. #27: ; //ESC - currently not supported
  1770. else
  1771. Result := Result + LChar;
  1772. if AEcho then begin
  1773. if AMask = '' then begin
  1774. Write(LChar, AByteEncoding);
  1775. end else begin
  1776. Write(AMask, AByteEncoding);
  1777. end;
  1778. end;
  1779. end;
  1780. end;
  1781. until LChar = LF;
  1782. // Remove CR trail
  1783. i := Length(Result);
  1784. while (i > 0) and CharIsInSet(Result, i, EOL) do begin
  1785. Dec(i);
  1786. end;
  1787. SetLength(Result, i);
  1788. if AEcho then begin
  1789. WriteLn(AByteEncoding);
  1790. end;
  1791. end;
  1792. //TODO: Add a time out (default to infinite) and event to pass data
  1793. //TODO: Add a max size argument as well.
  1794. //TODO: Add a case insensitive option
  1795. function TIdIOHandler.WaitFor(const AString: string; ARemoveFromBuffer: Boolean = True;
  1796. AInclusive: Boolean = False; AByteEncoding: IIdTextEncoding = nil;
  1797. ATimeout: Integer = IdTimeoutDefault): string;
  1798. var
  1799. LBytes: TIdBytes;
  1800. LPos: Integer;
  1801. begin
  1802. Result := '';
  1803. AByteEncoding := iif(AByteEncoding, FDefStringEncoding);
  1804. LBytes := ToBytes(AString, AByteEncoding);
  1805. LPos := 0;
  1806. repeat
  1807. LPos := InputBuffer.IndexOf(LBytes, LPos);
  1808. if LPos <> -1 then begin
  1809. if ARemoveFromBuffer and AInclusive then begin
  1810. Result := InputBuffer.ExtractToString(LPos+Length(LBytes), AByteEncoding);
  1811. end else begin
  1812. Result := InputBuffer.ExtractToString(LPos, AByteEncoding);
  1813. if ARemoveFromBuffer then begin
  1814. InputBuffer.Remove(Length(LBytes));
  1815. end;
  1816. if AInclusive then begin
  1817. Result := Result + AString;
  1818. end;
  1819. end;
  1820. Exit;
  1821. end;
  1822. LPos := IndyMax(0, InputBuffer.Size - (Length(LBytes)-1));
  1823. ReadFromSource(True, ATimeout, True);
  1824. until False;
  1825. end;
  1826. procedure TIdIOHandler.Capture(ADest: TStream; AByteEncoding: IIdTextEncoding = nil);
  1827. {$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
  1828. begin
  1829. Capture(ADest, '.', True, AByteEncoding); {do not localize}
  1830. end;
  1831. procedure TIdIOHandler.Capture(ADest: TStream; out VLineCount: Integer;
  1832. const ADelim: string = '.'; AUsesDotTransparency: Boolean = True;
  1833. AByteEncoding: IIdTextEncoding = nil);
  1834. {$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
  1835. begin
  1836. PerformCapture(ADest, VLineCount, ADelim, AUsesDotTransparency, AByteEncoding);
  1837. end;
  1838. procedure TIdIOHandler.Capture(ADest: TStream; ADelim: string;
  1839. AUsesDotTransparency: Boolean = True; AByteEncoding: IIdTextEncoding = nil);
  1840. var
  1841. LLineCount: Integer;
  1842. begin
  1843. PerformCapture(ADest, LLineCount, '.', AUsesDotTransparency, AByteEncoding); {do not localize}
  1844. end;
  1845. procedure TIdIOHandler.Capture(ADest: TStrings; out VLineCount: Integer;
  1846. const ADelim: string = '.'; AUsesDotTransparency: Boolean = True;
  1847. AByteEncoding: IIdTextEncoding = nil
  1848. );
  1849. {$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
  1850. begin
  1851. PerformCapture(ADest, VLineCount, ADelim, AUsesDotTransparency, AByteEncoding);
  1852. end;
  1853. procedure TIdIOHandler.Capture(ADest: TStrings; AByteEncoding: IIdTextEncoding = nil);
  1854. var
  1855. LLineCount: Integer;
  1856. begin
  1857. PerformCapture(ADest, LLineCount, '.', True, AByteEncoding); {do not localize}
  1858. end;
  1859. procedure TIdIOHandler.Capture(ADest: TStrings; const ADelim: string;
  1860. AUsesDotTransparency: Boolean = True; AByteEncoding: IIdTextEncoding = nil);
  1861. var
  1862. LLineCount: Integer;
  1863. begin
  1864. PerformCapture(ADest, LLineCount, ADelim, AUsesDotTransparency, AByteEncoding);
  1865. end;
  1866. procedure TIdIOHandler.InputBufferToStream(AStream: TStream; AByteCount: Integer = -1);
  1867. {$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
  1868. begin
  1869. FInputBuffer.ExtractToStream(AStream, AByteCount);
  1870. end;
  1871. function TIdIOHandler.InputBufferIsEmpty: Boolean;
  1872. {$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
  1873. begin
  1874. Result := FInputBuffer.Size = 0;
  1875. end;
  1876. procedure TIdIOHandler.Write(const ABuffer: TIdBytes; const ALength: Integer = -1;
  1877. const AOffset: Integer = 0);
  1878. var
  1879. LLength: Integer;
  1880. begin
  1881. LLength := IndyLength(ABuffer, ALength, AOffset);
  1882. if LLength > 0 then begin
  1883. if FWriteBuffer = nil then begin
  1884. WriteDirect(ABuffer, LLength, AOffset);
  1885. end else begin
  1886. // Write Buffering is enabled
  1887. FWriteBuffer.Write(ABuffer, LLength, AOffset);
  1888. if (FWriteBuffer.Size >= WriteBufferThreshold) and (WriteBufferThreshold > 0) then begin
  1889. repeat
  1890. WriteBufferFlush(WriteBufferThreshold);
  1891. until FWriteBuffer.Size < WriteBufferThreshold;
  1892. end;
  1893. end;
  1894. end;
  1895. end;
  1896. procedure TIdIOHandler.WriteRFCStrings(AStrings: TStrings; AWriteTerminator: Boolean = True;
  1897. AByteEncoding: IIdTextEncoding = nil);
  1898. var
  1899. i: Integer;
  1900. begin
  1901. AByteEncoding := iif(AByteEncoding, FDefStringEncoding);
  1902. for i := 0 to AStrings.Count - 1 do begin
  1903. WriteLnRFC(AStrings[i], AByteEncoding);
  1904. end;
  1905. if AWriteTerminator then begin
  1906. WriteLn('.', AByteEncoding);
  1907. end;
  1908. end;
  1909. function TIdIOHandler.WriteFile(const AFile: String; AEnableTransferFile: Boolean): Int64;
  1910. var
  1911. LStream: TStream;
  1912. {$IFDEF WIN32_OR_WIN64}
  1913. LOldErrorMode : Integer;
  1914. {$ENDIF}
  1915. begin
  1916. {$IFDEF WIN32_OR_WIN64}
  1917. LOldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
  1918. try
  1919. {$ENDIF}
  1920. if not FileExists(AFile) then begin
  1921. raise EIdFileNotFound.CreateFmt(RSFileNotFound, [AFile]);
  1922. end;
  1923. LStream := TIdReadFileExclusiveStream.Create(AFile);
  1924. try
  1925. Write(LStream);
  1926. Result := LStream.Size;
  1927. finally
  1928. LStream.Free;
  1929. end;
  1930. {$IFDEF WIN32_OR_WIN64}
  1931. finally
  1932. SetErrorMode(LOldErrorMode)
  1933. end;
  1934. {$ENDIF}
  1935. end;
  1936. function TIdIOHandler.WriteBufferingActive: Boolean;
  1937. {$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
  1938. begin
  1939. Result := FWriteBuffer <> nil;
  1940. end;
  1941. procedure TIdIOHandler.CloseGracefully;
  1942. begin
  1943. FClosedGracefully := True
  1944. end;
  1945. procedure TIdIOHandler.InterceptReceive(var VBuffer: TIdBytes);
  1946. var
  1947. // under ARC, convert a weak reference to a strong reference before working with it
  1948. LIntercept: TIdConnectionIntercept;
  1949. begin
  1950. LIntercept := Intercept;
  1951. if LIntercept <> nil then begin
  1952. LIntercept.Receive(VBuffer);
  1953. end;
  1954. end;
  1955. procedure TIdIOHandler.WriteBufferFlush;
  1956. begin
  1957. WriteBufferFlush(-1);
  1958. end;
  1959. procedure TIdIOHandler.WriteBufferOpen;
  1960. begin
  1961. WriteBufferOpen(-1);
  1962. end;
  1963. procedure TIdIOHandler.WriteDirect(const ABuffer: TIdBytes; const ALength: Integer = -1;
  1964. const AOffset: Integer = 0);
  1965. var
  1966. LTemp: TIdBytes;
  1967. LPos: Integer;
  1968. LSize: Integer;
  1969. LByteCount: Integer;
  1970. LLastError: Integer;
  1971. // under ARC, convert a weak reference to a strong reference before working with it
  1972. LIntercept: TIdConnectionIntercept;
  1973. begin
  1974. // Check if disconnected
  1975. CheckForDisconnect(True, True);
  1976. LIntercept := Intercept;
  1977. if LIntercept <> nil then begin
  1978. // TODO: pass offset/size parameters to the Intercept
  1979. // so that a copy is no longer needed here
  1980. LTemp := ToBytes(ABuffer, ALength, AOffset);
  1981. LIntercept.Send(LTemp);
  1982. {$IFDEF USE_OBJECT_ARC}LIntercept := nil;{$ENDIF}
  1983. LSize := Length(LTemp);
  1984. LPos := 0;
  1985. end else begin
  1986. LTemp := ABuffer;
  1987. LSize := IndyLength(LTemp, ALength, AOffset);
  1988. LPos := AOffset;
  1989. end;
  1990. while LSize > 0 do
  1991. begin
  1992. LByteCount := WriteDataToTarget(LTemp, LPos, LSize);
  1993. if LByteCount < 0 then
  1994. begin
  1995. LLastError := CheckForError(LByteCount);
  1996. if LLastError <> Id_WSAETIMEDOUT then begin
  1997. FClosedGracefully := True;
  1998. Close;
  1999. end;
  2000. RaiseError(LLastError);
  2001. end;
  2002. // TODO - Have a AntiFreeze param which allows the send to be split up so that process
  2003. // can be called more. Maybe a prop of the connection, MaxSendSize?
  2004. TIdAntiFreezeBase.DoProcess(False);
  2005. if LByteCount = 0 then begin
  2006. FClosedGracefully := True;
  2007. end;
  2008. // Check if other side disconnected
  2009. CheckForDisconnect;
  2010. DoWork(wmWrite, LByteCount);
  2011. Inc(LPos, LByteCount);
  2012. Dec(LSize, LByteCount);
  2013. end;
  2014. end;
  2015. initialization
  2016. finalization
  2017. FreeAndNil(GIOHandlerClassList)
  2018. end.