]>
git.saurik.com Git - apple/security.git/blob - Network/xfercore.cpp
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 // xfercore - core data transfer engine
23 #include <Security/debugging.h>
31 // Create an engine-level client object.
32 // @@@ Defer buffer allocation to mating?
33 // @@@ Defer state initialization to mating?
35 TransferEngine::Client::Client()
36 : mMode(invalidInput
), mAutoCopyOut(false),
37 mSink(NULL
), mSource(NULL
),
39 mReadBuffer(16384), mWriteBuffer(16384)
43 TransferEngine::Client::~Client()
49 // Add and remove clients to/from the engine
51 void TransferEngine::add(Client
*client
)
53 client
->io
= client
->fileDesc(); // punch master I/O down to Selector client level
54 Selector::add(client
->io
, *client
, input
| critical
); // initial registration
57 void TransferEngine::remove(Client
*client
)
60 if (!client
->mReadBuffer
.isEmpty())
61 secdebug("xferengine", "xfer %p(%d) HAD %ld BYTES READ LEFT",
62 client
, client
->fileDesc(), client
->mReadBuffer
.length());
63 if (!client
->mWriteBuffer
.isEmpty())
64 secdebug("xferengine", "xfer %p(%d) HAD %ld BYTES WRITE LEFT",
65 client
, client
->fileDesc(), client
->mWriteBuffer
.length());
67 if (client
->io
.fd () != -1) { // did we have a live socket?
68 Selector::remove(client
->io
);
71 client
->io
= FileDesc(); // invalidate
77 // In addition to the generic switcher (mode), there are variants that set associated
78 // information, such as sources/sinks.
80 void TransferEngine::Client::mode(InputMode newMode
)
82 secdebug("xferengine", "xfer %p(%d) switching to mode %d", this, fileDesc(), newMode
);
93 assert(false); // can't switch to these modes like that
97 void TransferEngine::Client::mode(Sink
&sink
, size_t byteCount
)
99 mMode
= autoReadInput
;
101 mResidualReadCount
= byteCount
;
102 secdebug("xferengine", "xfer %p(%d) switching to autoReadInput (%ld bytes)",
103 this, fileDesc(), byteCount
);
106 void TransferEngine::Client::mode(Source
&source
, size_t byteCount
)
108 assert (!mAutoCopyOut
); // no replacements, please
111 mResidualWriteCount
= byteCount
;
112 secdebug("xferengine", "xfer %p(%d) enabling autoCopyOut mode (%ld bytes)",
113 this, fileDesc(), byteCount
);
119 // Output methods. This queues output to be sent to the client's connection
120 // as soon as practical.
122 void TransferEngine::Client::printf(const char *format
, ...)
125 va_start(args
, format
);
126 vprintf(format
, args
);
130 void TransferEngine::Client::vprintf(const char *format
, va_list args
)
132 mWriteBuffer
.vprintf(format
, args
);
135 vsnprintf(buffer
, sizeof(buffer
), format
, args
);
136 secdebug("engineio", "%p(%d) <-- %s", this, fileDesc(), buffer
);
141 void TransferEngine::Client::printfe(const char *format
, ...)
144 va_start(args
, format
);
145 vprintfe(format
, args
);
149 void TransferEngine::Client::vprintfe(const char *format
, va_list args
)
151 mWriteBuffer
.vprintf(format
, args
);
152 mWriteBuffer
.printf("\r\n");
155 vsnprintf(buffer
, sizeof(buffer
), format
, args
);
156 secdebug("engineio", "%p(%d) <-- %s[CRNL]", this, fileDesc(), buffer
);
163 // Set output auto-flush mode. Think of this as a weak output-hold mode.
164 // If autoflush is off, we don't try hard to send data out immediately. If it's
165 // on, we send data as soon as it's generated.
166 // Calling flushOutput(true) always generates I/O as needed to send output
167 // data NOW (even if the mode was already on).
169 void TransferEngine::Client::flushOutput(bool autoFlush
)
171 mAutoFlush
= autoFlush
;
172 secdebug("engineio", "%p(%d) output flush %s", this, fileDesc(), autoFlush
? "on" : "off");
179 // StartOutput is called by output generators to get output flowing.
180 // It may generate output I/O, or hold things in buffers according to
183 void TransferEngine::Client::startOutput()
186 if (mAutoCopyOut
&& !mWriteBuffer
.isFull())
187 autoCopy(); // try to tack on some autoCopy output
188 if (!mWriteBuffer
.isEmpty()) {
189 mWriteBuffer
.write(*this);
190 if (mAutoFlush
|| !mWriteBuffer
.isEmpty()) { // possibly more output
191 enable(output
); // ask for output-drain notification
193 disable(output
); // no need for output-possible events
201 // Discard any data still in the input buffer.
202 // This is used to cope with unexpected garbage (protocol violations
203 // from the server), and shouldn't be used indiscriminately.
205 void TransferEngine::Client::flushInput()
207 if (!mReadBuffer
.isEmpty()) {
208 secdebug("engineio", "flushing %ld bytes of input", mReadBuffer
.length());
210 mInputFlushed
= true; // inhibit normal buffer ops
216 // Given that autoCopyOut mode is active, try to transfer some bytes
217 // into the write buffer. This is a lazy, fast push, suitable for tacking on
218 // when you are about to send data for some other reason.
219 // Returns the number of bytes retrieved from the auto-Source (possibly zero).
221 size_t TransferEngine::Client::autoCopy()
223 size_t len
= mWriteBuffer
.available(); //@@@ (true) ?
224 if (mResidualWriteCount
&& mResidualWriteCount
< len
)
225 len
= mResidualWriteCount
;
226 void *addr
; mWriteBuffer
.locatePut(addr
, len
);
227 mSource
->produce(addr
, len
);
228 secdebug("xferengine", "xfer %p(%d) autoCopyOut source delivered %ld bytes",
229 this, fileDesc(), len
);
230 mWriteBuffer
.usePut(len
);
236 // This is the notify function called by the IP Selector layer when I/O is possible.
237 // It runs the state machines for all current clients, calling their transit methods
240 void TransferEngine::Client::notify(int fd
, Type type
)
243 //@@@ Note: We do not currently do anything special about critical events.
245 if (type
& Selector::output
) {
246 // if we're in connecting mode
247 if (mMode
== connecting
) {
248 Socket s
; s
= fd
; // Socket(fd) means something different...
249 int error
= s
.error();
250 secdebug("xferengine", "xfer %p(%d) connect (errno %d)",
252 transit(connectionDone
, NULL
, error
);
256 //@@@ use high/low water marks here
257 if (mAutoCopyOut
&& !mWriteBuffer
.isFull()) {
258 if (autoCopy() == 0) {
259 switch (mSource
->state()) {
260 case Source::stalled
:
261 // ah well, maybe later
262 secdebug("xferengine", "xfer %p(%d) autoCopyOut source is stalled", this, fd
);
264 case Source::endOfData
:
265 mAutoCopyOut
= false; // done
266 secdebug("xferengine", "xfer %p(%d) autoCopyOut end of data", this, fd
);
267 if (mResidualWriteCount
> 0)
268 secdebug("xferengine", "xfer %p(%d) has %ld autoCopy bytes left",
269 this, fd
, mResidualWriteCount
);
270 transit(autoWriteDone
);
272 return; // transit removed us; stop now
279 if (mWriteBuffer
.isEmpty()) { // output possible, no output pending
280 secdebug("xferengine", "xfer %p(%d) disabling output (empty)", this, fd
);
282 } else { // stuff some more
283 size_t length
= mWriteBuffer
.write(*this);
284 secdebug("xferengine", "xfer %p(%d) writing %ld bytes", this, fd
, length
);
288 if (type
& Selector::input
) {
289 secdebug("xferengine", "xfer %p(%d) input ready %d bytes",
290 this, fd
, io
.iocget
<int>(FIONREAD
));
293 mInputFlushed
= false; // preset normal
295 //@@@ break out after partial buffer to give Equal Time to other transfers? good idea?!
296 if (!atEnd() && mReadBuffer
.read(*this) == 0 && !atEnd()) {
297 mReadBuffer
.read(*this, true);
300 if (mReadBuffer
.isEmpty() && atEnd()) {
309 if (!lineInputTransit())
310 return; // no full line; try again later
313 autoReadInputTransit();
314 if (mMode
!= autoIODone
)
316 // autoRead completed; fall through to autoIODone handling
318 mMode
= invalidInput
; // pre-mark error
319 transit(autoReadDone
); // notify; this must reset mode or exit
320 if (!isActive()) // if we're terminated...
321 return; // ... then go
322 assert(mMode
!= invalidInput
); // else enforce mode reset
326 // we should never be here. Selector gave us "read but not write" while connecting. FUBAR
328 secdebug("xferengine",
329 "fd %d input while connecting (errno=%d, type=%d)",
330 fd
, s
.error(), type
);
331 UnixError::throwMe(ECONNREFUSED
); // likely interpretation
334 secdebug("xferengine", "mode error in input sequencer (mode=%d)", mMode
);
337 if (!io
) // client has unhooked; clear buffer and exit loop
339 } while (!mReadBuffer
.isEmpty());
340 //@@@ feed back for more output here? But also see comments above...
341 //@@@ probably better to take the trip through the Selector
343 } catch (const CssmCommonError
&err
) {
346 transitError(UnixError::make(EIO
)); // best guess (could be anything)
350 void TransferEngine::Client::rawInputTransit()
352 // just shove it at the user
353 char *addr
; size_t length
= mReadBuffer
.length();
354 mReadBuffer
.locateGet(addr
, length
);
355 secdebug("engineio", "%p(%d) --> %d bytes RAW",
356 this, fileDesc(), io
.iocget
<int>(FIONREAD
));
357 transit(inputAvailable
, addr
, length
);
359 mReadBuffer
.useGet(length
);
362 bool TransferEngine::Client::lineInputTransit()
364 char *line
; size_t length
= mReadBuffer
.length();
365 mReadBuffer
.locateGet(line
, length
);
368 for (nl
= line
; nl
< line
+ length
&& *nl
!= '\n'; nl
++) ;
369 if (nl
== line
+ length
) // no end-of-line, wait for more
372 if (nl
> line
&& nl
[-1] == '\r') { // proper \r\n termination
373 nl
[-1] = '\0'; // terminate for transit convenience
374 secdebug("engineio", "%p(%d) --> %s", this, fileDesc(), line
);
375 transit(inputAvailable
, line
, nl
- line
- 1);
376 } else { // improper, tolerate
377 nl
[0] = '\0'; // terminate for transit convenience
378 secdebug("engineio", "%p(%d) [IMPROPER] --> %s", this, fileDesc(), line
);
379 transit(inputAvailable
, line
, nl
- line
);
382 mReadBuffer
.useGet(nl
- line
+ 1);
386 void TransferEngine::Client::autoReadInputTransit()
388 secdebug("xferengine", "xfer %p(%d) %ld pending %d available",
389 this, fileDesc(), mReadBuffer
.length(), io
.iocget
<int>(FIONREAD
));
390 void *data
; size_t length
= mReadBuffer
.length();
391 if (mResidualReadCount
&& mResidualReadCount
< length
)
392 length
= mResidualReadCount
;
393 mReadBuffer
.locateGet(data
, length
);
394 secdebug("engineio", "%p(%d) --> %ld bytes autoReadInput", this, fileDesc(), length
);
395 mSink
->consume(data
, length
);
397 mReadBuffer
.useGet(length
);
398 if (mResidualReadCount
&& (mResidualReadCount
-= length
) == 0)
404 // The (protected) tickle() method causes a one-time scan
405 // of the requesting client. This will simulate an input-ready event
406 // and possibly call the transit method.
407 // This is designed to be used from validate() or in other unusual
408 // external situations. Don't call this from within transit().
410 void TransferEngine::Client::tickle()
412 notify(io
, input
| critical
);
417 // The default read/write methods perform direct I/O on the underlying file descriptor.
419 size_t TransferEngine::Client::read(void *data
, size_t size
)
420 { return io
.read(data
, size
); }
422 size_t TransferEngine::Client::write(const void *data
, size_t size
)
423 { return io
.write(data
, size
); }
425 bool TransferEngine::Client::atEnd() const
426 { return io
.atEnd(); }
429 } // end namespace Network
430 } // end namespace Security