1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications.
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@netscape.com> (original author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include <stdlib.h>
40 : #include "nsHttp.h"
41 : #include "nsHttpPipeline.h"
42 : #include "nsHttpHandler.h"
43 : #include "nsIOService.h"
44 : #include "nsIRequest.h"
45 : #include "nsISocketTransport.h"
46 : #include "nsIStringStream.h"
47 : #include "nsIPipe.h"
48 : #include "nsCOMPtr.h"
49 : #include "nsComponentManagerUtils.h"
50 :
51 : #ifdef DEBUG
52 : #include "prthread.h"
53 : // defined by the socket transport service while active
54 : extern PRThread *gSocketThread;
55 : #endif
56 :
57 : //-----------------------------------------------------------------------------
58 : // nsHttpPushBackWriter
59 : //-----------------------------------------------------------------------------
60 :
61 : class nsHttpPushBackWriter : public nsAHttpSegmentWriter
62 : {
63 : public:
64 0 : nsHttpPushBackWriter(const char *buf, PRUint32 bufLen)
65 : : mBuf(buf)
66 0 : , mBufLen(bufLen)
67 0 : { }
68 0 : virtual ~nsHttpPushBackWriter() {}
69 :
70 0 : nsresult OnWriteSegment(char *buf, PRUint32 count, PRUint32 *countWritten)
71 : {
72 0 : if (mBufLen == 0)
73 0 : return NS_BASE_STREAM_CLOSED;
74 :
75 0 : if (count > mBufLen)
76 0 : count = mBufLen;
77 :
78 0 : memcpy(buf, mBuf, count);
79 :
80 0 : mBuf += count;
81 0 : mBufLen -= count;
82 0 : *countWritten = count;
83 0 : return NS_OK;
84 : }
85 :
86 : private:
87 : const char *mBuf;
88 : PRUint32 mBufLen;
89 : };
90 :
91 : //-----------------------------------------------------------------------------
92 : // nsHttpPipeline <public>
93 : //-----------------------------------------------------------------------------
94 :
95 0 : nsHttpPipeline::nsHttpPipeline()
96 : : mConnection(nsnull)
97 : , mStatus(NS_OK)
98 : , mRequestIsPartial(false)
99 : , mResponseIsPartial(false)
100 : , mClosed(false)
101 : , mPushBackBuf(nsnull)
102 : , mPushBackLen(0)
103 : , mPushBackMax(0)
104 : , mHttp1xTransactionCount(0)
105 : , mReceivingFromProgress(0)
106 : , mSendingToProgress(0)
107 0 : , mSuppressSendEvents(true)
108 : {
109 0 : }
110 :
111 0 : nsHttpPipeline::~nsHttpPipeline()
112 : {
113 : // make sure we aren't still holding onto any transactions!
114 0 : Close(NS_ERROR_ABORT);
115 :
116 0 : NS_IF_RELEASE(mConnection);
117 :
118 0 : if (mPushBackBuf)
119 0 : free(mPushBackBuf);
120 0 : }
121 :
122 : nsresult
123 0 : nsHttpPipeline::AddTransaction(nsAHttpTransaction *trans)
124 : {
125 0 : LOG(("nsHttpPipeline::AddTransaction [this=%x trans=%x]\n", this, trans));
126 :
127 0 : NS_ADDREF(trans);
128 0 : mRequestQ.AppendElement(trans);
129 :
130 0 : if (mConnection && !mClosed) {
131 0 : trans->SetConnection(this);
132 :
133 0 : if (mRequestQ.Length() == 1)
134 0 : mConnection->ResumeSend();
135 : }
136 :
137 0 : return NS_OK;
138 : }
139 :
140 : //-----------------------------------------------------------------------------
141 : // nsHttpPipeline::nsISupports
142 : //-----------------------------------------------------------------------------
143 :
144 0 : NS_IMPL_THREADSAFE_ADDREF(nsHttpPipeline)
145 0 : NS_IMPL_THREADSAFE_RELEASE(nsHttpPipeline)
146 :
147 : // multiple inheritance fun :-)
148 0 : NS_INTERFACE_MAP_BEGIN(nsHttpPipeline)
149 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsAHttpConnection)
150 0 : NS_INTERFACE_MAP_END
151 :
152 :
153 : //-----------------------------------------------------------------------------
154 : // nsHttpPipeline::nsAHttpConnection
155 : //-----------------------------------------------------------------------------
156 :
157 : nsresult
158 0 : nsHttpPipeline::OnHeadersAvailable(nsAHttpTransaction *trans,
159 : nsHttpRequestHead *requestHead,
160 : nsHttpResponseHead *responseHead,
161 : bool *reset)
162 : {
163 0 : LOG(("nsHttpPipeline::OnHeadersAvailable [this=%x]\n", this));
164 :
165 0 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
166 0 : NS_ASSERTION(mConnection, "no connection");
167 :
168 : // trans has now received its response headers; forward to the real connection
169 0 : return mConnection->OnHeadersAvailable(trans, requestHead, responseHead, reset);
170 : }
171 :
172 : nsresult
173 0 : nsHttpPipeline::ResumeSend()
174 : {
175 0 : if (mConnection)
176 0 : return mConnection->ResumeSend();
177 0 : return NS_ERROR_UNEXPECTED;
178 : }
179 :
180 : nsresult
181 0 : nsHttpPipeline::ResumeRecv()
182 : {
183 0 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
184 0 : NS_ASSERTION(mConnection, "no connection");
185 0 : return mConnection->ResumeRecv();
186 : }
187 :
188 : void
189 0 : nsHttpPipeline::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
190 : {
191 0 : LOG(("nsHttpPipeline::CloseTransaction [this=%x trans=%x reason=%x]\n",
192 : this, trans, reason));
193 :
194 0 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
195 0 : NS_ASSERTION(NS_FAILED(reason), "expecting failure code");
196 :
197 : // the specified transaction is to be closed with the given "reason"
198 :
199 : PRInt32 index;
200 0 : bool killPipeline = false;
201 :
202 0 : index = mRequestQ.IndexOf(trans);
203 0 : if (index >= 0) {
204 0 : if (index == 0 && mRequestIsPartial) {
205 : // the transaction is in the request queue. check to see if any of
206 : // its data has been written out yet.
207 0 : killPipeline = true;
208 : }
209 0 : mRequestQ.RemoveElementAt(index);
210 : }
211 : else {
212 0 : index = mResponseQ.IndexOf(trans);
213 0 : if (index >= 0)
214 0 : mResponseQ.RemoveElementAt(index);
215 : // while we could avoid killing the pipeline if this transaction is the
216 : // last transaction in the pipeline, there doesn't seem to be that much
217 : // value in doing so. most likely if this transaction is going away,
218 : // the others will be shortly as well.
219 0 : killPipeline = true;
220 : }
221 :
222 0 : trans->Close(reason);
223 0 : NS_RELEASE(trans);
224 :
225 0 : if (killPipeline) {
226 0 : if (mConnection)
227 0 : mConnection->CloseTransaction(this, reason);
228 : else
229 0 : Close(reason);
230 : }
231 0 : }
232 :
233 : void
234 0 : nsHttpPipeline::GetConnectionInfo(nsHttpConnectionInfo **result)
235 : {
236 0 : NS_ASSERTION(mConnection, "no connection");
237 0 : mConnection->GetConnectionInfo(result);
238 0 : }
239 :
240 : nsresult
241 0 : nsHttpPipeline::TakeTransport(nsISocketTransport **aTransport,
242 : nsIAsyncInputStream **aInputStream,
243 : nsIAsyncOutputStream **aOutputStream)
244 : {
245 0 : return mConnection->TakeTransport(aTransport, aInputStream, aOutputStream);
246 : }
247 :
248 : void
249 0 : nsHttpPipeline::GetSecurityInfo(nsISupports **result)
250 : {
251 0 : NS_ASSERTION(mConnection, "no connection");
252 0 : mConnection->GetSecurityInfo(result);
253 0 : }
254 :
255 : bool
256 0 : nsHttpPipeline::IsPersistent()
257 : {
258 0 : return true; // pipelining requires this
259 : }
260 :
261 : bool
262 0 : nsHttpPipeline::IsReused()
263 : {
264 0 : return true; // pipelining requires this
265 : }
266 :
267 : nsresult
268 0 : nsHttpPipeline::PushBack(const char *data, PRUint32 length)
269 : {
270 0 : LOG(("nsHttpPipeline::PushBack [this=%x len=%u]\n", this, length));
271 :
272 0 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
273 0 : NS_ASSERTION(mPushBackLen == 0, "push back buffer already has data!");
274 :
275 : // If we have no chance for a pipeline (e.g. due to an Upgrade)
276 : // then push this data down to original connection
277 0 : if (!mConnection->IsPersistent())
278 0 : return mConnection->PushBack(data, length);
279 :
280 : // PushBack is called recursively from WriteSegments
281 :
282 : // XXX we have a design decision to make here. either we buffer the data
283 : // and process it when we return to WriteSegments, or we attempt to move
284 : // onto the next transaction from here. doing so adds complexity with the
285 : // benefit of eliminating the extra buffer copy. the buffer is at most
286 : // 4096 bytes, so it is really unclear if there is any value in the added
287 : // complexity. besides simplicity, buffering this data has the advantage
288 : // that we'll call close on the transaction sooner, which will wake up
289 : // the HTTP channel sooner to continue with its work.
290 :
291 0 : if (!mPushBackBuf) {
292 0 : mPushBackMax = length;
293 0 : mPushBackBuf = (char *) malloc(mPushBackMax);
294 0 : if (!mPushBackBuf)
295 0 : return NS_ERROR_OUT_OF_MEMORY;
296 : }
297 0 : else if (length > mPushBackMax) {
298 : // grow push back buffer as necessary.
299 0 : NS_ASSERTION(length <= nsIOService::gDefaultSegmentSize, "too big");
300 0 : mPushBackMax = length;
301 0 : mPushBackBuf = (char *) realloc(mPushBackBuf, mPushBackMax);
302 0 : if (!mPushBackBuf)
303 0 : return NS_ERROR_OUT_OF_MEMORY;
304 : }
305 :
306 0 : memcpy(mPushBackBuf, data, length);
307 0 : mPushBackLen = length;
308 :
309 0 : return NS_OK;
310 : }
311 :
312 : bool
313 0 : nsHttpPipeline::LastTransactionExpectedNoContent()
314 : {
315 0 : NS_ABORT_IF_FALSE(mConnection, "no connection");
316 0 : return mConnection->LastTransactionExpectedNoContent();
317 : }
318 :
319 : void
320 0 : nsHttpPipeline::SetLastTransactionExpectedNoContent(bool val)
321 : {
322 0 : NS_ABORT_IF_FALSE(mConnection, "no connection");
323 0 : mConnection->SetLastTransactionExpectedNoContent(val);
324 0 : }
325 :
326 : nsHttpConnection *
327 0 : nsHttpPipeline::TakeHttpConnection()
328 : {
329 0 : if (mConnection)
330 0 : return mConnection->TakeHttpConnection();
331 0 : return nsnull;
332 : }
333 :
334 : nsISocketTransport *
335 0 : nsHttpPipeline::Transport()
336 : {
337 0 : if (!mConnection)
338 0 : return nsnull;
339 0 : return mConnection->Transport();
340 : }
341 :
342 : void
343 0 : nsHttpPipeline::SetSSLConnectFailed()
344 : {
345 0 : nsAHttpTransaction *trans = Request(0);
346 :
347 0 : if (trans)
348 0 : trans->SetSSLConnectFailed();
349 0 : }
350 :
351 : nsHttpRequestHead *
352 0 : nsHttpPipeline::RequestHead()
353 : {
354 0 : nsAHttpTransaction *trans = Request(0);
355 :
356 0 : if (trans)
357 0 : return trans->RequestHead();
358 0 : return nsnull;
359 : }
360 :
361 : PRUint32
362 0 : nsHttpPipeline::Http1xTransactionCount()
363 : {
364 0 : return mHttp1xTransactionCount;
365 : }
366 :
367 : nsresult
368 0 : nsHttpPipeline::TakeSubTransactions(
369 : nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
370 : {
371 0 : LOG(("nsHttpPipeline::TakeSubTransactions [this=%p]\n", this));
372 :
373 0 : if (mResponseQ.Length() || mRequestIsPartial)
374 0 : return NS_ERROR_ALREADY_OPENED;
375 :
376 : // request queue could be empty if it was already canceled, in which
377 : // case it is safe to treat this as a case without any
378 : // sub-transactions.
379 0 : if (!mRequestQ.Length())
380 0 : return NS_ERROR_NOT_IMPLEMENTED;
381 :
382 0 : PRInt32 i, count = mRequestQ.Length();
383 0 : for (i = 0; i < count; ++i) {
384 0 : nsAHttpTransaction *trans = Request(i);
385 0 : outTransactions.AppendElement(trans);
386 0 : NS_RELEASE(trans);
387 : }
388 0 : mRequestQ.Clear();
389 :
390 0 : LOG((" took %d\n", count));
391 0 : return NS_OK;
392 : }
393 :
394 : //-----------------------------------------------------------------------------
395 : // nsHttpPipeline::nsAHttpConnection
396 : //-----------------------------------------------------------------------------
397 :
398 : void
399 0 : nsHttpPipeline::SetConnection(nsAHttpConnection *conn)
400 : {
401 0 : LOG(("nsHttpPipeline::SetConnection [this=%x conn=%x]\n", this, conn));
402 :
403 0 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
404 0 : NS_ASSERTION(!mConnection, "already have a connection");
405 :
406 0 : NS_IF_ADDREF(mConnection = conn);
407 :
408 0 : PRInt32 i, count = mRequestQ.Length();
409 0 : for (i=0; i<count; ++i)
410 0 : Request(i)->SetConnection(this);
411 0 : }
412 :
413 : nsAHttpConnection *
414 0 : nsHttpPipeline::Connection()
415 : {
416 0 : LOG(("nsHttpPipeline::Connection [this=%x conn=%x]\n", this, mConnection));
417 :
418 0 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
419 0 : return mConnection;
420 : }
421 :
422 : void
423 0 : nsHttpPipeline::GetSecurityCallbacks(nsIInterfaceRequestor **result,
424 : nsIEventTarget **target)
425 : {
426 0 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
427 :
428 : // return security callbacks from first request
429 0 : nsAHttpTransaction *trans = Request(0);
430 0 : if (trans)
431 0 : trans->GetSecurityCallbacks(result, target);
432 : else {
433 0 : *result = nsnull;
434 0 : if (target)
435 0 : *target = nsnull;
436 : }
437 0 : }
438 :
439 : void
440 0 : nsHttpPipeline::OnTransportStatus(nsITransport* transport,
441 : nsresult status, PRUint64 progress)
442 : {
443 0 : LOG(("nsHttpPipeline::OnStatus [this=%x status=%x progress=%llu]\n",
444 : this, status, progress));
445 :
446 0 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
447 :
448 : nsAHttpTransaction *trans;
449 : PRInt32 i, count;
450 :
451 0 : switch (status) {
452 :
453 : case NS_NET_STATUS_RESOLVING_HOST:
454 : case NS_NET_STATUS_RESOLVED_HOST:
455 : case NS_NET_STATUS_CONNECTING_TO:
456 : case NS_NET_STATUS_CONNECTED_TO:
457 : // These should only appear at most once per pipeline.
458 : // Deliver to the first transaction.
459 :
460 0 : trans = Request(0);
461 0 : if (!trans)
462 0 : trans = Response(0);
463 0 : if (trans)
464 0 : trans->OnTransportStatus(transport, status, progress);
465 :
466 0 : break;
467 :
468 : case NS_NET_STATUS_SENDING_TO:
469 : // This is generated by the socket transport when (part) of
470 : // a transaction is written out
471 : //
472 : // In pipelining this is generated out of FillSendBuf(), but it cannot do
473 : // so until the connection is confirmed by CONNECTED_TO.
474 : // See patch for bug 196827.
475 : //
476 :
477 0 : if (mSuppressSendEvents) {
478 0 : mSuppressSendEvents = false;
479 :
480 : // catch up by sending the event to all the transactions that have
481 : // moved from request to response and any that have been partially
482 : // sent. Also send WAITING_FOR to those that were completely sent
483 0 : count = mResponseQ.Length();
484 0 : for (i = 0; i < count; ++i) {
485 0 : Response(i)->OnTransportStatus(transport,
486 : NS_NET_STATUS_SENDING_TO,
487 0 : progress);
488 0 : Response(i)->OnTransportStatus(transport,
489 : NS_NET_STATUS_WAITING_FOR,
490 0 : progress);
491 : }
492 0 : if (mRequestIsPartial && Request(0))
493 0 : Request(0)->OnTransportStatus(transport,
494 : NS_NET_STATUS_SENDING_TO,
495 0 : progress);
496 0 : mSendingToProgress = progress;
497 : }
498 : // otherwise ignore it
499 0 : break;
500 :
501 : case NS_NET_STATUS_WAITING_FOR:
502 : // Created by nsHttpConnection when request pipeline has been totally
503 : // sent. Ignore it here because it is simulated in FillSendBuf() when
504 : // a request is moved from request to response.
505 :
506 : // ignore it
507 0 : break;
508 :
509 : case NS_NET_STATUS_RECEIVING_FROM:
510 : // Forward this only to the transaction currently recieving data. It is
511 : // normally generated by the socket transport, but can also
512 : // be repeated by the pushbackwriter if necessary.
513 0 : mReceivingFromProgress = progress;
514 0 : if (Response(0))
515 0 : Response(0)->OnTransportStatus(transport, status, progress);
516 0 : break;
517 :
518 : default:
519 : // forward other notifications to all request transactions
520 0 : count = mRequestQ.Length();
521 0 : for (i = 0; i < count; ++i)
522 0 : Request(i)->OnTransportStatus(transport, status, progress);
523 0 : break;
524 : }
525 0 : }
526 :
527 : bool
528 0 : nsHttpPipeline::IsDone()
529 : {
530 0 : bool done = true;
531 :
532 0 : PRUint32 i, count = mRequestQ.Length();
533 0 : for (i = 0; done && (i < count); i++)
534 0 : done = Request(i)->IsDone();
535 :
536 0 : count = mResponseQ.Length();
537 0 : for (i = 0; done && (i < count); i++)
538 0 : done = Response(i)->IsDone();
539 :
540 0 : return done;
541 : }
542 :
543 : nsresult
544 0 : nsHttpPipeline::Status()
545 : {
546 0 : return mStatus;
547 : }
548 :
549 : PRUint32
550 0 : nsHttpPipeline::Available()
551 : {
552 0 : PRUint32 result = 0;
553 :
554 0 : PRInt32 i, count = mRequestQ.Length();
555 0 : for (i=0; i<count; ++i)
556 0 : result += Request(i)->Available();
557 0 : return result;
558 : }
559 :
560 : NS_METHOD
561 0 : nsHttpPipeline::ReadFromPipe(nsIInputStream *stream,
562 : void *closure,
563 : const char *buf,
564 : PRUint32 offset,
565 : PRUint32 count,
566 : PRUint32 *countRead)
567 : {
568 0 : nsHttpPipeline *self = (nsHttpPipeline *) closure;
569 0 : return self->mReader->OnReadSegment(buf, count, countRead);
570 : }
571 :
572 : nsresult
573 0 : nsHttpPipeline::ReadSegments(nsAHttpSegmentReader *reader,
574 : PRUint32 count,
575 : PRUint32 *countRead)
576 : {
577 0 : LOG(("nsHttpPipeline::ReadSegments [this=%x count=%u]\n", this, count));
578 :
579 0 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
580 :
581 0 : if (mClosed) {
582 0 : *countRead = 0;
583 0 : return mStatus;
584 : }
585 :
586 : nsresult rv;
587 0 : PRUint32 avail = 0;
588 0 : if (mSendBufIn) {
589 0 : rv = mSendBufIn->Available(&avail);
590 0 : if (NS_FAILED(rv)) return rv;
591 : }
592 :
593 0 : if (avail == 0) {
594 0 : rv = FillSendBuf();
595 0 : if (NS_FAILED(rv)) return rv;
596 :
597 0 : rv = mSendBufIn->Available(&avail);
598 0 : if (NS_FAILED(rv)) return rv;
599 :
600 : // return EOF if send buffer is empty
601 0 : if (avail == 0) {
602 0 : *countRead = 0;
603 0 : return NS_OK;
604 : }
605 : }
606 :
607 : // read no more than what was requested
608 0 : if (avail > count)
609 0 : avail = count;
610 :
611 0 : mReader = reader;
612 :
613 0 : rv = mSendBufIn->ReadSegments(ReadFromPipe, this, avail, countRead);
614 :
615 0 : mReader = nsnull;
616 0 : return rv;
617 : }
618 :
619 : nsresult
620 0 : nsHttpPipeline::WriteSegments(nsAHttpSegmentWriter *writer,
621 : PRUint32 count,
622 : PRUint32 *countWritten)
623 : {
624 0 : LOG(("nsHttpPipeline::WriteSegments [this=%x count=%u]\n", this, count));
625 :
626 0 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
627 :
628 0 : if (mClosed)
629 0 : return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
630 :
631 : nsAHttpTransaction *trans;
632 : nsresult rv;
633 :
634 0 : trans = Response(0);
635 0 : if (!trans) {
636 0 : if (mRequestQ.Length() > 0)
637 0 : rv = NS_BASE_STREAM_WOULD_BLOCK;
638 : else
639 0 : rv = NS_BASE_STREAM_CLOSED;
640 : }
641 : else {
642 : //
643 : // ask the transaction to consume data from the connection.
644 : // PushBack may be called recursively.
645 : //
646 0 : rv = trans->WriteSegments(writer, count, countWritten);
647 :
648 0 : if (rv == NS_BASE_STREAM_CLOSED || trans->IsDone()) {
649 0 : trans->Close(NS_OK);
650 0 : NS_RELEASE(trans);
651 0 : mResponseQ.RemoveElementAt(0);
652 0 : mResponseIsPartial = false;
653 0 : ++mHttp1xTransactionCount;
654 :
655 : // ask the connection manager to add additional transactions
656 : // to our pipeline.
657 0 : gHttpHandler->ConnMgr()->AddTransactionToPipeline(this);
658 : }
659 : else
660 0 : mResponseIsPartial = true;
661 : }
662 :
663 0 : if (mPushBackLen) {
664 0 : nsHttpPushBackWriter writer(mPushBackBuf, mPushBackLen);
665 0 : PRUint32 len = mPushBackLen, n;
666 0 : mPushBackLen = 0;
667 :
668 : // This progress notification has previously been sent from
669 : // the socket transport code, but it was delivered to the
670 : // previous transaction on the pipeline.
671 0 : nsITransport *transport = Transport();
672 0 : if (transport)
673 : OnTransportStatus(transport,
674 : nsISocketTransport::STATUS_RECEIVING_FROM,
675 0 : mReceivingFromProgress);
676 :
677 : // the push back buffer is never larger than NS_HTTP_SEGMENT_SIZE,
678 : // so we are guaranteed that the next response will eat the entire
679 : // push back buffer (even though it might again call PushBack).
680 0 : rv = WriteSegments(&writer, len, &n);
681 : }
682 :
683 0 : return rv;
684 : }
685 :
686 : void
687 0 : nsHttpPipeline::Close(nsresult reason)
688 : {
689 0 : LOG(("nsHttpPipeline::Close [this=%x reason=%x]\n", this, reason));
690 :
691 0 : if (mClosed) {
692 0 : LOG((" already closed\n"));
693 0 : return;
694 : }
695 :
696 : // the connection is going away!
697 0 : mStatus = reason;
698 0 : mClosed = true;
699 :
700 : PRUint32 i, count;
701 : nsAHttpTransaction *trans;
702 :
703 : // any pending requests can ignore this error and be restarted
704 0 : count = mRequestQ.Length();
705 0 : for (i=0; i<count; ++i) {
706 0 : trans = Request(i);
707 0 : trans->Close(NS_ERROR_NET_RESET);
708 0 : NS_RELEASE(trans);
709 : }
710 0 : mRequestQ.Clear();
711 :
712 0 : trans = Response(0);
713 0 : if (trans) {
714 : // if the current response is partially complete, then it cannot be
715 : // restarted and will have to fail with the status of the connection.
716 0 : if (mResponseIsPartial)
717 0 : trans->Close(reason);
718 : else
719 0 : trans->Close(NS_ERROR_NET_RESET);
720 0 : NS_RELEASE(trans);
721 :
722 : // any remaining pending responses can be restarted
723 0 : count = mResponseQ.Length();
724 0 : for (i=1; i<count; ++i) {
725 0 : trans = Response(i);
726 0 : trans->Close(NS_ERROR_NET_RESET);
727 0 : NS_RELEASE(trans);
728 : }
729 0 : mResponseQ.Clear();
730 : }
731 :
732 : }
733 :
734 : nsresult
735 0 : nsHttpPipeline::OnReadSegment(const char *segment,
736 : PRUint32 count,
737 : PRUint32 *countRead)
738 : {
739 0 : return mSendBufOut->Write(segment, count, countRead);
740 : }
741 :
742 : nsresult
743 0 : nsHttpPipeline::FillSendBuf()
744 : {
745 : // reads from request queue, moving transactions to response queue
746 : // when they have been completely read.
747 :
748 : nsresult rv;
749 :
750 0 : if (!mSendBufIn) {
751 : // allocate a single-segment pipe
752 0 : rv = NS_NewPipe(getter_AddRefs(mSendBufIn),
753 0 : getter_AddRefs(mSendBufOut),
754 : nsIOService::gDefaultSegmentSize, /* segment size */
755 : nsIOService::gDefaultSegmentSize, /* max size */
756 0 : true, true);
757 0 : if (NS_FAILED(rv)) return rv;
758 : }
759 :
760 : PRUint32 n, avail;
761 : nsAHttpTransaction *trans;
762 0 : nsITransport *transport = Transport();
763 :
764 0 : while ((trans = Request(0)) != nsnull) {
765 0 : avail = trans->Available();
766 0 : if (avail) {
767 0 : rv = trans->ReadSegments(this, avail, &n);
768 0 : if (NS_FAILED(rv)) return rv;
769 :
770 0 : if (n == 0) {
771 0 : LOG(("send pipe is full"));
772 0 : break;
773 : }
774 :
775 0 : mSendingToProgress += n;
776 0 : if (!mSuppressSendEvents && transport) {
777 : // Simulate a SENDING_TO event
778 : trans->OnTransportStatus(transport,
779 : NS_NET_STATUS_SENDING_TO,
780 0 : mSendingToProgress);
781 : }
782 : }
783 :
784 0 : avail = trans->Available();
785 0 : if (avail == 0) {
786 : // move transaction from request queue to response queue
787 0 : mRequestQ.RemoveElementAt(0);
788 0 : mResponseQ.AppendElement(trans);
789 0 : mRequestIsPartial = false;
790 :
791 0 : if (!mSuppressSendEvents && transport) {
792 : // Simulate a WAITING_FOR event
793 : trans->OnTransportStatus(transport,
794 : NS_NET_STATUS_WAITING_FOR,
795 0 : mSendingToProgress);
796 : }
797 : }
798 : else
799 0 : mRequestIsPartial = true;
800 : }
801 0 : return NS_OK;
802 : }
|