nsresult nsHttpTransaction::Init(PRUint8 caps, nsHttpConnectionInfo *cinfo, nsHttpRequestHead *requestHead, nsIInputStream *requestBody, PRBool requestBodyHasHeaders, nsIEventTarget *target, nsIInterfaceRequestor *callbacks, nsITransportEventSink *eventsink, nsIAsyncInputStream **responseBody) { nsresult rv; LOG(("nsHttpTransaction::Init [this=%x caps=%x]\n", this, caps)); NS_ASSERTION(cinfo, "ouch"); NS_ASSERTION(requestHead, "ouch"); NS_ASSERTION(target, "ouch"); // create transport event sink proxy that coalesces all events rv = net_NewTransportEventSinkProxy(getter_AddRefs(mTransportSink), eventsink, target, PR_TRUE); if (NS_FAILED(rv)) return rv; mActivityDistributor = do_GetService(NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; PRBool active; rv = mActivityDistributor->GetIsActive(&active); if (NS_SUCCEEDED(rv) && active) { // there are some observers registered at activity distributor, gather // nsISupports for the channel that called Init() mChannel = do_QueryInterface(eventsink); LOG(("nsHttpTransaction::Init() " \ "mActivityDistributor is active " \ "this=%x", this)); } else // there is no observer, so don't use it mActivityDistributor = nsnull; NS_ADDREF(mConnInfo = cinfo); mCallbacks = callbacks; mConsumerTarget = target; mCaps = caps; if (requestHead->Method() == nsHttp::Head) mNoContent = PR_TRUE; // Make sure that there is "Content-Length: 0" header in the requestHead // in case of POST and PUT methods when there is no requestBody and // requestHead doesn't contain "Transfer-Encoding" header. // // RFC1945 section 7.2.2: // HTTP/1.0 requests containing an entity body must include a valid // Content-Length header field. // // RFC2616 section 4.4: // For compatibility with HTTP/1.0 applications, HTTP/1.1 requests // containing a message-body MUST include a valid Content-Length header // field unless the server is known to be HTTP/1.1 compliant. if ((requestHead->Method() == nsHttp::Post || requestHead->Method() == nsHttp::Put) && !requestBody && !requestHead->PeekHeader(nsHttp::Transfer_Encoding)) { requestHead->SetHeader(nsHttp::Content_Length, NS_LITERAL_CSTRING("0")); } // grab a weak reference to the request head mRequestHead = requestHead; // make sure we eliminate any proxy specific headers from // the request if we are talking HTTPS via a SSL tunnel. PRBool pruneProxyHeaders = cinfo->UsingSSL() && cinfo->UsingHttpProxy(); mReqHeaderBuf.Truncate(); requestHead->Flatten(mReqHeaderBuf, pruneProxyHeaders); #if defined(PR_LOGGING) if (LOG3_ENABLED()) { LOG3(("http request [\n")); LogHeaders(mReqHeaderBuf.get()); LOG3(("]\n")); } #endif // If the request body does not include headers or if there is no request // body, then we must add the header/body separator manually. if (!requestBodyHasHeaders || !requestBody) mReqHeaderBuf.AppendLiteral("\r\n"); // report the request header if (mActivityDistributor) mActivityDistributor->ObserveActivity( mChannel, NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION, NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_HEADER, PR_Now(), LL_ZERO, mReqHeaderBuf); // Create a string stream for the request header buf (the stream holds // a non-owning reference to the request header data, so we MUST keep // mReqHeaderBuf around). nsCOMPtr<nsIInputStream> headers; rv = NS_NewByteInputStream(getter_AddRefs(headers), mReqHeaderBuf.get(), mReqHeaderBuf.Length()); if (NS_FAILED(rv)) return rv; if (requestBody) { mHasRequestBody = PR_TRUE; // wrap the headers and request body in a multiplexed input stream. nsCOMPtr<nsIMultiplexInputStream> multi = do_CreateInstance(kMultiplexInputStream, &rv); if (NS_FAILED(rv)) return rv; rv = multi->AppendStream(headers); if (NS_FAILED(rv)) return rv; rv = multi->AppendStream(requestBody); if (NS_FAILED(rv)) return rv; // wrap the multiplexed input stream with a buffered input stream, so // that we write data in the largest chunks possible. this is actually // necessary to workaround some common server bugs (see bug 137155). rv = NS_NewBufferedInputStream(getter_AddRefs(mRequestStream), multi, nsIOService::gDefaultSegmentSize); if (NS_FAILED(rv)) return rv; } else mRequestStream = headers; rv = mRequestStream->Available(&mRequestSize); if (NS_FAILED(rv)) return rv; // create pipe for response stream rv = NS_NewPipe2(getter_AddRefs(mPipeIn), getter_AddRefs(mPipeOut), PR_TRUE, PR_TRUE, nsIOService::gDefaultSegmentSize, nsIOService::gDefaultSegmentCount, nsIOService::gBufferCache); if (NS_FAILED(rv)) return rv; NS_ADDREF(*responseBody = mPipeIn); return NS_OK; }
nsresult nsHttpTransaction::Init(PRUint8 caps, nsHttpConnectionInfo *cinfo, nsHttpRequestHead *requestHead, nsIInputStream *requestBody, PRBool requestBodyHasHeaders, nsIEventTarget *target, nsIInterfaceRequestor *callbacks, nsITransportEventSink *eventsink, nsIAsyncInputStream **responseBody) { nsresult rv; LOG(("nsHttpTransaction::Init [this=%x caps=%x]\n", this, caps)); NS_ASSERTION(cinfo, "ouch"); NS_ASSERTION(requestHead, "ouch"); NS_ASSERTION(target, "ouch"); // create transport event sink proxy that coalesces all events rv = net_NewTransportEventSinkProxy(getter_AddRefs(mTransportSink), eventsink, target, PR_TRUE); if (NS_FAILED(rv)) return rv; // try to get the nsIHttpActivityObserver distributor mActivityDistributor = do_GetService(NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID, &rv); // mActivityDistributor may not be valid if (NS_SUCCEEDED(rv) && mActivityDistributor) { // the service is valid, now check if it is active PRBool active; rv = mActivityDistributor->GetIsActive(&active); if (NS_SUCCEEDED(rv) && active) { // the service is valid and active, gather nsISupports // for the channel that called Init() mChannel = do_QueryInterface(eventsink); LOG(("nsHttpTransaction::Init() " \ "mActivityDistributor is active " \ "this=%x", this)); } else // the interface in valid but not active, so don't use it mActivityDistributor = nsnull; } NS_ADDREF(mConnInfo = cinfo); mCallbacks = callbacks; mConsumerTarget = target; mCaps = caps; if (requestHead->Method() == nsHttp::Head) mNoContent = PR_TRUE; // grab a weak reference to the request head mRequestHead = requestHead; // make sure we eliminate any proxy specific headers from // the request if we are talking HTTPS via a SSL tunnel. PRBool pruneProxyHeaders = cinfo->UsingSSL() && cinfo->UsingHttpProxy(); mReqHeaderBuf.Truncate(); requestHead->Flatten(mReqHeaderBuf, pruneProxyHeaders); #if defined(PR_LOGGING) if (LOG3_ENABLED()) { LOG3(("http request [\n")); LogHeaders(mReqHeaderBuf.get()); LOG3(("]\n")); } #endif // If the request body does not include headers or if there is no request // body, then we must add the header/body separator manually. if (!requestBodyHasHeaders || !requestBody) mReqHeaderBuf.AppendLiteral("\r\n"); // report the request header if (mActivityDistributor) mActivityDistributor->ObserveActivity( mChannel, NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION, NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_HEADER, LL_ZERO, LL_ZERO, mReqHeaderBuf); // Create a string stream for the request header buf (the stream holds // a non-owning reference to the request header data, so we MUST keep // mReqHeaderBuf around). nsCOMPtr<nsIInputStream> headers; rv = NS_NewByteInputStream(getter_AddRefs(headers), mReqHeaderBuf.get(), mReqHeaderBuf.Length()); if (NS_FAILED(rv)) return rv; if (requestBody) { mHasRequestBody = PR_TRUE; // wrap the headers and request body in a multiplexed input stream. nsCOMPtr<nsIMultiplexInputStream> multi = do_CreateInstance(kMultiplexInputStream, &rv); if (NS_FAILED(rv)) return rv; rv = multi->AppendStream(headers); if (NS_FAILED(rv)) return rv; rv = multi->AppendStream(requestBody); if (NS_FAILED(rv)) return rv; // wrap the multiplexed input stream with a buffered input stream, so // that we write data in the largest chunks possible. this is actually // necessary to workaround some common server bugs (see bug 137155). rv = NS_NewBufferedInputStream(getter_AddRefs(mRequestStream), multi, NET_DEFAULT_SEGMENT_SIZE); if (NS_FAILED(rv)) return rv; } else mRequestStream = headers; rv = mRequestStream->Available(&mRequestSize); if (NS_FAILED(rv)) return rv; // create pipe for response stream rv = NS_NewPipe2(getter_AddRefs(mPipeIn), getter_AddRefs(mPipeOut), PR_TRUE, PR_TRUE, NS_HTTP_SEGMENT_SIZE, NS_HTTP_SEGMENT_COUNT, nsIOService::gBufferCache); if (NS_FAILED(rv)) return rv; NS_ADDREF(*responseBody = mPipeIn); return NS_OK; }