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