Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | #ifndef nsSocketTransport2_h__ |
michael@0 | 6 | #define nsSocketTransport2_h__ |
michael@0 | 7 | |
michael@0 | 8 | #ifdef DEBUG_darinf |
michael@0 | 9 | #define ENABLE_SOCKET_TRACING |
michael@0 | 10 | #endif |
michael@0 | 11 | |
michael@0 | 12 | #include "mozilla/Mutex.h" |
michael@0 | 13 | #include "nsSocketTransportService2.h" |
michael@0 | 14 | #include "nsString.h" |
michael@0 | 15 | #include "nsCOMPtr.h" |
michael@0 | 16 | |
michael@0 | 17 | #include "nsISocketTransport.h" |
michael@0 | 18 | #include "nsIAsyncInputStream.h" |
michael@0 | 19 | #include "nsIAsyncOutputStream.h" |
michael@0 | 20 | #include "nsIDNSListener.h" |
michael@0 | 21 | #include "nsIClassInfo.h" |
michael@0 | 22 | #include "mozilla/net/DNS.h" |
michael@0 | 23 | #include "nsASocketHandler.h" |
michael@0 | 24 | |
michael@0 | 25 | #include "prerror.h" |
michael@0 | 26 | #include "nsAutoPtr.h" |
michael@0 | 27 | |
michael@0 | 28 | class nsSocketTransport; |
michael@0 | 29 | class nsICancelable; |
michael@0 | 30 | class nsIDNSRecord; |
michael@0 | 31 | class nsIInterfaceRequestor; |
michael@0 | 32 | |
michael@0 | 33 | nsresult |
michael@0 | 34 | ErrorAccordingToNSPR(PRErrorCode errorCode); |
michael@0 | 35 | |
michael@0 | 36 | //----------------------------------------------------------------------------- |
michael@0 | 37 | |
michael@0 | 38 | // after this short interval, we will return to PR_Poll |
michael@0 | 39 | #define NS_SOCKET_CONNECT_TIMEOUT PR_MillisecondsToInterval(20) |
michael@0 | 40 | |
michael@0 | 41 | //----------------------------------------------------------------------------- |
michael@0 | 42 | |
michael@0 | 43 | class nsSocketInputStream : public nsIAsyncInputStream |
michael@0 | 44 | { |
michael@0 | 45 | public: |
michael@0 | 46 | NS_DECL_ISUPPORTS_INHERITED |
michael@0 | 47 | NS_DECL_NSIINPUTSTREAM |
michael@0 | 48 | NS_DECL_NSIASYNCINPUTSTREAM |
michael@0 | 49 | |
michael@0 | 50 | nsSocketInputStream(nsSocketTransport *); |
michael@0 | 51 | virtual ~nsSocketInputStream(); |
michael@0 | 52 | |
michael@0 | 53 | bool IsReferenced() { return mReaderRefCnt > 0; } |
michael@0 | 54 | nsresult Condition() { return mCondition; } |
michael@0 | 55 | uint64_t ByteCount() { return mByteCount; } |
michael@0 | 56 | |
michael@0 | 57 | // called by the socket transport on the socket thread... |
michael@0 | 58 | void OnSocketReady(nsresult condition); |
michael@0 | 59 | |
michael@0 | 60 | private: |
michael@0 | 61 | nsSocketTransport *mTransport; |
michael@0 | 62 | mozilla::ThreadSafeAutoRefCnt mReaderRefCnt; |
michael@0 | 63 | |
michael@0 | 64 | // access to these is protected by mTransport->mLock |
michael@0 | 65 | nsresult mCondition; |
michael@0 | 66 | nsCOMPtr<nsIInputStreamCallback> mCallback; |
michael@0 | 67 | uint32_t mCallbackFlags; |
michael@0 | 68 | uint64_t mByteCount; |
michael@0 | 69 | }; |
michael@0 | 70 | |
michael@0 | 71 | //----------------------------------------------------------------------------- |
michael@0 | 72 | |
michael@0 | 73 | class nsSocketOutputStream : public nsIAsyncOutputStream |
michael@0 | 74 | { |
michael@0 | 75 | public: |
michael@0 | 76 | NS_DECL_ISUPPORTS_INHERITED |
michael@0 | 77 | NS_DECL_NSIOUTPUTSTREAM |
michael@0 | 78 | NS_DECL_NSIASYNCOUTPUTSTREAM |
michael@0 | 79 | |
michael@0 | 80 | nsSocketOutputStream(nsSocketTransport *); |
michael@0 | 81 | virtual ~nsSocketOutputStream(); |
michael@0 | 82 | |
michael@0 | 83 | bool IsReferenced() { return mWriterRefCnt > 0; } |
michael@0 | 84 | nsresult Condition() { return mCondition; } |
michael@0 | 85 | uint64_t ByteCount() { return mByteCount; } |
michael@0 | 86 | |
michael@0 | 87 | // called by the socket transport on the socket thread... |
michael@0 | 88 | void OnSocketReady(nsresult condition); |
michael@0 | 89 | |
michael@0 | 90 | private: |
michael@0 | 91 | static NS_METHOD WriteFromSegments(nsIInputStream *, void *, |
michael@0 | 92 | const char *, uint32_t offset, |
michael@0 | 93 | uint32_t count, uint32_t *countRead); |
michael@0 | 94 | |
michael@0 | 95 | nsSocketTransport *mTransport; |
michael@0 | 96 | mozilla::ThreadSafeAutoRefCnt mWriterRefCnt; |
michael@0 | 97 | |
michael@0 | 98 | // access to these is protected by mTransport->mLock |
michael@0 | 99 | nsresult mCondition; |
michael@0 | 100 | nsCOMPtr<nsIOutputStreamCallback> mCallback; |
michael@0 | 101 | uint32_t mCallbackFlags; |
michael@0 | 102 | uint64_t mByteCount; |
michael@0 | 103 | }; |
michael@0 | 104 | |
michael@0 | 105 | //----------------------------------------------------------------------------- |
michael@0 | 106 | |
michael@0 | 107 | class nsSocketTransport : public nsASocketHandler |
michael@0 | 108 | , public nsISocketTransport |
michael@0 | 109 | , public nsIDNSListener |
michael@0 | 110 | , public nsIClassInfo |
michael@0 | 111 | { |
michael@0 | 112 | typedef mozilla::Mutex Mutex; |
michael@0 | 113 | |
michael@0 | 114 | public: |
michael@0 | 115 | NS_DECL_THREADSAFE_ISUPPORTS |
michael@0 | 116 | NS_DECL_NSITRANSPORT |
michael@0 | 117 | NS_DECL_NSISOCKETTRANSPORT |
michael@0 | 118 | NS_DECL_NSIDNSLISTENER |
michael@0 | 119 | NS_DECL_NSICLASSINFO |
michael@0 | 120 | |
michael@0 | 121 | nsSocketTransport(); |
michael@0 | 122 | |
michael@0 | 123 | // this method instructs the socket transport to open a socket of the |
michael@0 | 124 | // given type(s) to the given host or proxy. |
michael@0 | 125 | nsresult Init(const char **socketTypes, uint32_t typeCount, |
michael@0 | 126 | const nsACString &host, uint16_t port, |
michael@0 | 127 | nsIProxyInfo *proxyInfo); |
michael@0 | 128 | |
michael@0 | 129 | // this method instructs the socket transport to use an already connected |
michael@0 | 130 | // socket with the given address. |
michael@0 | 131 | nsresult InitWithConnectedSocket(PRFileDesc *socketFD, |
michael@0 | 132 | const mozilla::net::NetAddr *addr); |
michael@0 | 133 | |
michael@0 | 134 | // This method instructs the socket transport to open a socket |
michael@0 | 135 | // connected to the given Unix domain address. We can only create |
michael@0 | 136 | // unlayered, simple, stream sockets. |
michael@0 | 137 | nsresult InitWithFilename(const char *filename); |
michael@0 | 138 | |
michael@0 | 139 | // nsASocketHandler methods: |
michael@0 | 140 | void OnSocketReady(PRFileDesc *, int16_t outFlags); |
michael@0 | 141 | void OnSocketDetached(PRFileDesc *); |
michael@0 | 142 | void IsLocal(bool *aIsLocal); |
michael@0 | 143 | void OnKeepaliveEnabledPrefChange(bool aEnabled) MOZ_OVERRIDE MOZ_FINAL; |
michael@0 | 144 | |
michael@0 | 145 | // called when a socket event is handled |
michael@0 | 146 | void OnSocketEvent(uint32_t type, nsresult status, nsISupports *param); |
michael@0 | 147 | |
michael@0 | 148 | uint64_t ByteCountReceived() { return mInput.ByteCount(); } |
michael@0 | 149 | uint64_t ByteCountSent() { return mOutput.ByteCount(); } |
michael@0 | 150 | protected: |
michael@0 | 151 | |
michael@0 | 152 | virtual ~nsSocketTransport(); |
michael@0 | 153 | |
michael@0 | 154 | private: |
michael@0 | 155 | |
michael@0 | 156 | // event types |
michael@0 | 157 | enum { |
michael@0 | 158 | MSG_ENSURE_CONNECT, |
michael@0 | 159 | MSG_DNS_LOOKUP_COMPLETE, |
michael@0 | 160 | MSG_RETRY_INIT_SOCKET, |
michael@0 | 161 | MSG_TIMEOUT_CHANGED, |
michael@0 | 162 | MSG_INPUT_CLOSED, |
michael@0 | 163 | MSG_INPUT_PENDING, |
michael@0 | 164 | MSG_OUTPUT_CLOSED, |
michael@0 | 165 | MSG_OUTPUT_PENDING |
michael@0 | 166 | }; |
michael@0 | 167 | nsresult PostEvent(uint32_t type, nsresult status = NS_OK, nsISupports *param = nullptr); |
michael@0 | 168 | |
michael@0 | 169 | enum { |
michael@0 | 170 | STATE_CLOSED, |
michael@0 | 171 | STATE_IDLE, |
michael@0 | 172 | STATE_RESOLVING, |
michael@0 | 173 | STATE_CONNECTING, |
michael@0 | 174 | STATE_TRANSFERRING, |
michael@0 | 175 | STATE_SENDINGGET, |
michael@0 | 176 | STATE_SENTGET |
michael@0 | 177 | }; |
michael@0 | 178 | |
michael@0 | 179 | // Safer way to get and automatically release PRFileDesc objects. |
michael@0 | 180 | class MOZ_STACK_CLASS PRFileDescAutoLock |
michael@0 | 181 | { |
michael@0 | 182 | public: |
michael@0 | 183 | typedef mozilla::MutexAutoLock MutexAutoLock; |
michael@0 | 184 | |
michael@0 | 185 | PRFileDescAutoLock(nsSocketTransport *aSocketTransport, |
michael@0 | 186 | nsresult *aConditionWhileLocked = nullptr) |
michael@0 | 187 | : mSocketTransport(aSocketTransport) |
michael@0 | 188 | , mFd(nullptr) |
michael@0 | 189 | { |
michael@0 | 190 | MOZ_ASSERT(aSocketTransport); |
michael@0 | 191 | MutexAutoLock lock(mSocketTransport->mLock); |
michael@0 | 192 | if (aConditionWhileLocked) { |
michael@0 | 193 | *aConditionWhileLocked = mSocketTransport->mCondition; |
michael@0 | 194 | if (NS_FAILED(mSocketTransport->mCondition)) { |
michael@0 | 195 | return; |
michael@0 | 196 | } |
michael@0 | 197 | } |
michael@0 | 198 | mFd = mSocketTransport->GetFD_Locked(); |
michael@0 | 199 | } |
michael@0 | 200 | ~PRFileDescAutoLock() { |
michael@0 | 201 | MutexAutoLock lock(mSocketTransport->mLock); |
michael@0 | 202 | if (mFd) { |
michael@0 | 203 | mSocketTransport->ReleaseFD_Locked(mFd); |
michael@0 | 204 | } |
michael@0 | 205 | } |
michael@0 | 206 | bool IsInitialized() { |
michael@0 | 207 | return mFd; |
michael@0 | 208 | } |
michael@0 | 209 | operator PRFileDesc*() { |
michael@0 | 210 | return mFd; |
michael@0 | 211 | } |
michael@0 | 212 | nsresult SetKeepaliveEnabled(bool aEnable); |
michael@0 | 213 | nsresult SetKeepaliveVals(bool aEnabled, int aIdleTime, |
michael@0 | 214 | int aRetryInterval, int aProbeCount); |
michael@0 | 215 | private: |
michael@0 | 216 | operator PRFileDescAutoLock*() { return nullptr; } |
michael@0 | 217 | |
michael@0 | 218 | // Weak ptr to nsSocketTransport since this is a stack class only. |
michael@0 | 219 | nsSocketTransport *mSocketTransport; |
michael@0 | 220 | PRFileDesc *mFd; |
michael@0 | 221 | }; |
michael@0 | 222 | friend class PRFileDescAutoLock; |
michael@0 | 223 | |
michael@0 | 224 | class LockedPRFileDesc |
michael@0 | 225 | { |
michael@0 | 226 | public: |
michael@0 | 227 | LockedPRFileDesc(nsSocketTransport *aSocketTransport) |
michael@0 | 228 | : mSocketTransport(aSocketTransport) |
michael@0 | 229 | , mFd(nullptr) |
michael@0 | 230 | { |
michael@0 | 231 | MOZ_ASSERT(aSocketTransport); |
michael@0 | 232 | } |
michael@0 | 233 | ~LockedPRFileDesc() {} |
michael@0 | 234 | bool IsInitialized() { |
michael@0 | 235 | return mFd; |
michael@0 | 236 | } |
michael@0 | 237 | LockedPRFileDesc& operator=(PRFileDesc *aFd) { |
michael@0 | 238 | mSocketTransport->mLock.AssertCurrentThreadOwns(); |
michael@0 | 239 | mFd = aFd; |
michael@0 | 240 | return *this; |
michael@0 | 241 | } |
michael@0 | 242 | operator PRFileDesc*() { |
michael@0 | 243 | if (mSocketTransport->mAttached) { |
michael@0 | 244 | mSocketTransport->mLock.AssertCurrentThreadOwns(); |
michael@0 | 245 | } |
michael@0 | 246 | return mFd; |
michael@0 | 247 | } |
michael@0 | 248 | bool operator==(PRFileDesc *aFd) { |
michael@0 | 249 | mSocketTransport->mLock.AssertCurrentThreadOwns(); |
michael@0 | 250 | return mFd == aFd; |
michael@0 | 251 | } |
michael@0 | 252 | private: |
michael@0 | 253 | operator LockedPRFileDesc*() { return nullptr; } |
michael@0 | 254 | // Weak ptr to nsSocketTransport since it owns this class. |
michael@0 | 255 | nsSocketTransport *mSocketTransport; |
michael@0 | 256 | PRFileDesc *mFd; |
michael@0 | 257 | }; |
michael@0 | 258 | friend class LockedPRFileDesc; |
michael@0 | 259 | |
michael@0 | 260 | //------------------------------------------------------------------------- |
michael@0 | 261 | // these members are "set" at initialization time and are never modified |
michael@0 | 262 | // afterwards. this allows them to be safely accessed from any thread. |
michael@0 | 263 | //------------------------------------------------------------------------- |
michael@0 | 264 | |
michael@0 | 265 | // socket type info: |
michael@0 | 266 | char **mTypes; |
michael@0 | 267 | uint32_t mTypeCount; |
michael@0 | 268 | nsCString mHost; |
michael@0 | 269 | uint16_t mPort; |
michael@0 | 270 | bool mHttpsProxy; |
michael@0 | 271 | |
michael@0 | 272 | nsCOMPtr<nsIProxyInfo> mProxyInfo; |
michael@0 | 273 | bool mProxyUse; |
michael@0 | 274 | bool mProxyTransparent; |
michael@0 | 275 | bool mProxyTransparentResolvesHost; |
michael@0 | 276 | uint32_t mConnectionFlags; |
michael@0 | 277 | |
michael@0 | 278 | uint16_t SocketPort(); |
michael@0 | 279 | const nsCString &SocketHost(); |
michael@0 | 280 | nsCString mProxyHostCache; // for SocketHost() only |
michael@0 | 281 | |
michael@0 | 282 | //------------------------------------------------------------------------- |
michael@0 | 283 | // members accessible only on the socket transport thread: |
michael@0 | 284 | // (the exception being initialization/shutdown time) |
michael@0 | 285 | //------------------------------------------------------------------------- |
michael@0 | 286 | |
michael@0 | 287 | // socket state vars: |
michael@0 | 288 | uint32_t mState; // STATE_??? flags |
michael@0 | 289 | bool mAttached; |
michael@0 | 290 | bool mInputClosed; |
michael@0 | 291 | bool mOutputClosed; |
michael@0 | 292 | |
michael@0 | 293 | // this flag is used to determine if the results of a host lookup arrive |
michael@0 | 294 | // recursively or not. this flag is not protected by any lock. |
michael@0 | 295 | bool mResolving; |
michael@0 | 296 | |
michael@0 | 297 | nsCOMPtr<nsICancelable> mDNSRequest; |
michael@0 | 298 | nsCOMPtr<nsIDNSRecord> mDNSRecord; |
michael@0 | 299 | |
michael@0 | 300 | // mNetAddr is valid from GetPeerAddr() once we have |
michael@0 | 301 | // reached STATE_TRANSFERRING. It must not change after that. |
michael@0 | 302 | mozilla::net::NetAddr mNetAddr; |
michael@0 | 303 | bool mNetAddrIsSet; |
michael@0 | 304 | |
michael@0 | 305 | // socket methods (these can only be called on the socket thread): |
michael@0 | 306 | |
michael@0 | 307 | void SendStatus(nsresult status); |
michael@0 | 308 | nsresult ResolveHost(); |
michael@0 | 309 | nsresult BuildSocket(PRFileDesc *&, bool &, bool &); |
michael@0 | 310 | nsresult InitiateSocket(); |
michael@0 | 311 | bool RecoverFromError(); |
michael@0 | 312 | |
michael@0 | 313 | void OnMsgInputPending() |
michael@0 | 314 | { |
michael@0 | 315 | if (mState == STATE_TRANSFERRING) |
michael@0 | 316 | mPollFlags |= (PR_POLL_READ | PR_POLL_EXCEPT); |
michael@0 | 317 | } |
michael@0 | 318 | void OnMsgOutputPending() |
michael@0 | 319 | { |
michael@0 | 320 | if (mState == STATE_TRANSFERRING) |
michael@0 | 321 | mPollFlags |= (PR_POLL_WRITE | PR_POLL_EXCEPT); |
michael@0 | 322 | } |
michael@0 | 323 | void OnMsgInputClosed(nsresult reason); |
michael@0 | 324 | void OnMsgOutputClosed(nsresult reason); |
michael@0 | 325 | |
michael@0 | 326 | // called when the socket is connected |
michael@0 | 327 | void OnSocketConnected(); |
michael@0 | 328 | |
michael@0 | 329 | //------------------------------------------------------------------------- |
michael@0 | 330 | // socket input/output objects. these may be accessed on any thread with |
michael@0 | 331 | // the exception of some specific methods (XXX). |
michael@0 | 332 | |
michael@0 | 333 | Mutex mLock; // protects members in this section. |
michael@0 | 334 | LockedPRFileDesc mFD; |
michael@0 | 335 | nsrefcnt mFDref; // mFD is closed when mFDref goes to zero. |
michael@0 | 336 | bool mFDconnected; // mFD is available to consumer when TRUE. |
michael@0 | 337 | |
michael@0 | 338 | // A delete protector reference to gSocketTransportService held for lifetime |
michael@0 | 339 | // of 'this'. Sometimes used interchangably with gSocketTransportService due |
michael@0 | 340 | // to scoping. |
michael@0 | 341 | nsRefPtr<nsSocketTransportService> mSocketTransportService; |
michael@0 | 342 | |
michael@0 | 343 | nsCOMPtr<nsIInterfaceRequestor> mCallbacks; |
michael@0 | 344 | nsCOMPtr<nsITransportEventSink> mEventSink; |
michael@0 | 345 | nsCOMPtr<nsISupports> mSecInfo; |
michael@0 | 346 | |
michael@0 | 347 | nsSocketInputStream mInput; |
michael@0 | 348 | nsSocketOutputStream mOutput; |
michael@0 | 349 | |
michael@0 | 350 | friend class nsSocketInputStream; |
michael@0 | 351 | friend class nsSocketOutputStream; |
michael@0 | 352 | |
michael@0 | 353 | // socket timeouts are not protected by any lock. |
michael@0 | 354 | uint16_t mTimeouts[2]; |
michael@0 | 355 | |
michael@0 | 356 | // QoS setting for socket |
michael@0 | 357 | uint8_t mQoSBits; |
michael@0 | 358 | |
michael@0 | 359 | // |
michael@0 | 360 | // mFD access methods: called with mLock held. |
michael@0 | 361 | // |
michael@0 | 362 | PRFileDesc *GetFD_Locked(); |
michael@0 | 363 | void ReleaseFD_Locked(PRFileDesc *fd); |
michael@0 | 364 | |
michael@0 | 365 | // |
michael@0 | 366 | // stream state changes (called outside mLock): |
michael@0 | 367 | // |
michael@0 | 368 | void OnInputClosed(nsresult reason) |
michael@0 | 369 | { |
michael@0 | 370 | // no need to post an event if called on the socket thread |
michael@0 | 371 | if (PR_GetCurrentThread() == gSocketThread) |
michael@0 | 372 | OnMsgInputClosed(reason); |
michael@0 | 373 | else |
michael@0 | 374 | PostEvent(MSG_INPUT_CLOSED, reason); |
michael@0 | 375 | } |
michael@0 | 376 | void OnInputPending() |
michael@0 | 377 | { |
michael@0 | 378 | // no need to post an event if called on the socket thread |
michael@0 | 379 | if (PR_GetCurrentThread() == gSocketThread) |
michael@0 | 380 | OnMsgInputPending(); |
michael@0 | 381 | else |
michael@0 | 382 | PostEvent(MSG_INPUT_PENDING); |
michael@0 | 383 | } |
michael@0 | 384 | void OnOutputClosed(nsresult reason) |
michael@0 | 385 | { |
michael@0 | 386 | // no need to post an event if called on the socket thread |
michael@0 | 387 | if (PR_GetCurrentThread() == gSocketThread) |
michael@0 | 388 | OnMsgOutputClosed(reason); // XXX need to not be inside lock! |
michael@0 | 389 | else |
michael@0 | 390 | PostEvent(MSG_OUTPUT_CLOSED, reason); |
michael@0 | 391 | } |
michael@0 | 392 | void OnOutputPending() |
michael@0 | 393 | { |
michael@0 | 394 | // no need to post an event if called on the socket thread |
michael@0 | 395 | if (PR_GetCurrentThread() == gSocketThread) |
michael@0 | 396 | OnMsgOutputPending(); |
michael@0 | 397 | else |
michael@0 | 398 | PostEvent(MSG_OUTPUT_PENDING); |
michael@0 | 399 | } |
michael@0 | 400 | |
michael@0 | 401 | #ifdef ENABLE_SOCKET_TRACING |
michael@0 | 402 | void TraceInBuf(const char *buf, int32_t n); |
michael@0 | 403 | void TraceOutBuf(const char *buf, int32_t n); |
michael@0 | 404 | #endif |
michael@0 | 405 | |
michael@0 | 406 | // Reads prefs to get default keepalive config. |
michael@0 | 407 | nsresult EnsureKeepaliveValsAreInitialized(); |
michael@0 | 408 | |
michael@0 | 409 | // Groups calls to fd.SetKeepaliveEnabled and fd.SetKeepaliveVals. |
michael@0 | 410 | nsresult SetKeepaliveEnabledInternal(bool aEnable); |
michael@0 | 411 | |
michael@0 | 412 | // True if keepalive has been enabled by the socket owner. Note: Keepalive |
michael@0 | 413 | // must also be enabled globally for it to be enabled in TCP. |
michael@0 | 414 | bool mKeepaliveEnabled; |
michael@0 | 415 | |
michael@0 | 416 | // Keepalive config (support varies by platform). |
michael@0 | 417 | int32_t mKeepaliveIdleTimeS; |
michael@0 | 418 | int32_t mKeepaliveRetryIntervalS; |
michael@0 | 419 | int32_t mKeepaliveProbeCount; |
michael@0 | 420 | }; |
michael@0 | 421 | |
michael@0 | 422 | #endif // !nsSocketTransport_h__ |