#ifndef CRYPTOPP_NETWORK_H #define CRYPTOPP_NETWORK_H #include "config.h" #ifdef HIGHRES_TIMER_AVAILABLE #include "filters.h" #include "hrtimer.h" #include NAMESPACE_BEGIN(CryptoPP) class LimitedBandwidth { public: LimitedBandwidth(lword maxBytesPerSecond = 0) : m_maxBytesPerSecond(maxBytesPerSecond), m_timer(Timer::MILLISECONDS) , m_nextTransceiveTime(0) { m_timer.StartTimer(); } lword GetMaxBytesPerSecond() const { return m_maxBytesPerSecond; } void SetMaxBytesPerSecond(lword v) { m_maxBytesPerSecond = v; } lword ComputeCurrentTransceiveLimit(); double TimeToNextTransceive(); void NoteTransceive(lword size); public: /*! GetWaitObjects() must be called despite the 0 return from GetMaxWaitObjectCount(); the 0 is because the ScheduleEvent() method is used instead of adding a wait object */ unsigned int GetMaxWaitObjectCount() const { return 0; } void GetWaitObjects(WaitObjectContainer &container, const CallStack &callStack); private: lword m_maxBytesPerSecond; typedef std::deque > OpQueue; OpQueue m_ops; Timer m_timer; double m_nextTransceiveTime; void ComputeNextTransceiveTime(); double GetCurTimeAndCleanUp(); }; //! a Source class that can pump from a device for a specified amount of time. class CRYPTOPP_NO_VTABLE NonblockingSource : public AutoSignaling, public LimitedBandwidth { public: NonblockingSource(BufferedTransformation *attachment) : m_messageEndSent(false) , m_doPumpBlocked(false), m_blockedBySpeedLimit(false) {Detach(attachment);} //! \name NONBLOCKING SOURCE //@{ //! pump up to maxSize bytes using at most maxTime milliseconds /*! If checkDelimiter is true, pump up to delimiter, which itself is not extracted or pumped. */ size_t GeneralPump2(lword &byteCount, bool blockingOutput=true, unsigned long maxTime=INFINITE_TIME, bool checkDelimiter=false, byte delimiter='\n'); lword GeneralPump(lword maxSize=LWORD_MAX, unsigned long maxTime=INFINITE_TIME, bool checkDelimiter=false, byte delimiter='\n') { GeneralPump2(maxSize, true, maxTime, checkDelimiter, delimiter); return maxSize; } lword TimedPump(unsigned long maxTime) {return GeneralPump(LWORD_MAX, maxTime);} lword PumpLine(byte delimiter='\n', lword maxSize=1024) {return GeneralPump(maxSize, INFINITE_TIME, true, delimiter);} size_t Pump2(lword &byteCount, bool blocking=true) {return GeneralPump2(byteCount, blocking, blocking ? INFINITE_TIME : 0);} size_t PumpMessages2(unsigned int &messageCount, bool blocking=true); //@} protected: virtual size_t DoPump(lword &byteCount, bool blockingOutput, unsigned long maxTime, bool checkDelimiter, byte delimiter) =0; bool BlockedBySpeedLimit() const { return m_blockedBySpeedLimit; } private: bool m_messageEndSent, m_doPumpBlocked, m_blockedBySpeedLimit; }; //! Network Receiver class CRYPTOPP_NO_VTABLE NetworkReceiver : public Waitable { public: virtual bool MustWaitToReceive() {return false;} virtual bool MustWaitForResult() {return false;} //! receive data from network source, returns whether result is immediately available virtual bool Receive(byte* buf, size_t bufLen) =0; virtual unsigned int GetReceiveResult() =0; virtual bool EofReceived() const =0; }; class CRYPTOPP_NO_VTABLE NonblockingSinkInfo { public: virtual ~NonblockingSinkInfo() {} virtual size_t GetMaxBufferSize() const =0; virtual size_t GetCurrentBufferSize() const =0; virtual bool EofPending() const =0; //! compute the current speed of this sink in bytes per second virtual float ComputeCurrentSpeed() =0; //! get the maximum observed speed of this sink in bytes per second virtual float GetMaxObservedSpeed() const =0; }; //! a Sink class that queues input and can std::flush to a device for a specified amount of time. class CRYPTOPP_NO_VTABLE NonblockingSink : public Sink, public NonblockingSinkInfo, public LimitedBandwidth { public: NonblockingSink() : m_blockedBySpeedLimit(false) {} bool IsolatedFlush(bool hardFlush, bool blocking); //! std::flush to device for no more than maxTime milliseconds /*! This function will repeatedly attempt to std::flush data to some device, until the queue is empty, or a total of maxTime milliseconds have elapsed. If maxTime == 0, at least one attempt will be made to std::flush some data, but it is likely that not all queued data will be std::flushed, even if the device is ready to receive more data without waiting. If you want to std::flush as much data as possible without waiting for the device, call this function in a loop. For example: while (sink.TimedFlush(0) > 0) {} \return number of bytes std::flushed */ lword TimedFlush(unsigned long maxTime, size_t targetSize = 0); virtual void SetMaxBufferSize(size_t maxBufferSize) =0; //! set a bound which will cause sink to std::flush if exceeded by GetCurrentBufferSize() virtual void SetAutoFlushBound(size_t bound) =0; protected: virtual lword DoFlush(unsigned long maxTime, size_t targetSize) = 0; bool BlockedBySpeedLimit() const { return m_blockedBySpeedLimit; } private: bool m_blockedBySpeedLimit; }; //! Network Sender class CRYPTOPP_NO_VTABLE NetworkSender : public Waitable { public: virtual bool MustWaitToSend() {return false;} virtual bool MustWaitForResult() {return false;} virtual void Send(const byte* buf, size_t bufLen) =0; virtual unsigned int GetSendResult() =0; virtual bool MustWaitForEof() {return false;} virtual void SendEof() =0; virtual bool EofSent() {return false;} // implement if MustWaitForEof() == true }; //! Network Source class CRYPTOPP_NO_VTABLE NetworkSource : public NonblockingSource { public: NetworkSource(BufferedTransformation *attachment); unsigned int GetMaxWaitObjectCount() const; void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); bool SourceExhausted() const {return m_dataBegin == m_dataEnd && GetReceiver().EofReceived();} protected: size_t DoPump(lword &byteCount, bool blockingOutput, unsigned long maxTime, bool checkDelimiter, byte delimiter); virtual NetworkReceiver & AccessReceiver() =0; const NetworkReceiver & GetReceiver() const {return const_cast(this)->AccessReceiver();} private: SecByteBlock m_buf; size_t m_putSize, m_dataBegin, m_dataEnd; bool m_waitingForResult, m_outputBlocked; }; //! Network Sink class CRYPTOPP_NO_VTABLE NetworkSink : public NonblockingSink { public: NetworkSink(unsigned int maxBufferSize, unsigned int autoFlushBound); unsigned int GetMaxWaitObjectCount() const; void GetWaitObjects(WaitObjectContainer &container, CallStack const& callStack); size_t Put2(const byte *inString, size_t length, int messageEnd, bool blocking); void SetMaxBufferSize(size_t maxBufferSize) {m_maxBufferSize = maxBufferSize; m_buffer.SetNodeSize(UnsignedMin(maxBufferSize, 16U*1024U+256U));} void SetAutoFlushBound(size_t bound) {m_autoFlushBound = bound;} size_t GetMaxBufferSize() const {return m_maxBufferSize;} size_t GetCurrentBufferSize() const {return (size_t)m_buffer.CurrentSize();} void ClearBuffer() { m_buffer.Clear(); } bool EofPending() const { return m_eofState > EOF_NONE && m_eofState < EOF_DONE; } //! compute the current speed of this sink in bytes per second float ComputeCurrentSpeed(); //! get the maximum observed speed of this sink in bytes per second float GetMaxObservedSpeed() const; protected: lword DoFlush(unsigned long maxTime, size_t targetSize); virtual NetworkSender & AccessSender() =0; const NetworkSender & GetSender() const {return const_cast(this)->AccessSender();} private: enum EofState { EOF_NONE, EOF_PENDING_SEND, EOF_PENDING_DELIVERY, EOF_DONE }; size_t m_maxBufferSize, m_autoFlushBound; bool m_needSendResult, m_wasBlocked; EofState m_eofState; ByteQueue m_buffer; size_t m_skipBytes; Timer m_speedTimer; float m_byteCountSinceLastTimerReset, m_currentSpeed, m_maxObservedSpeed; }; NAMESPACE_END #endif // #ifdef HIGHRES_TIMER_AVAILABLE #endif