nsresult SpdyStream::ParseHttpRequestHeaders(const char *buf, PRUint32 avail, PRUint32 *countUsed) { // Returns NS_OK even if the headers are incomplete // set mSynFrameComplete flag if they are complete NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread"); NS_ABORT_IF_FALSE(mUpstreamState == GENERATING_SYN_STREAM, "wrong state"); LOG3(("SpdyStream::ParseHttpRequestHeaders %p avail=%d state=%x", this, avail, mUpstreamState)); mFlatHttpRequestHeaders.Append(buf, avail); // We can use the simple double crlf because firefox is the // only client we are parsing PRInt32 endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n"); if (endHeader == -1) { // We don't have all the headers yet LOG3(("SpdyStream::ParseHttpRequestHeaders %p " "Need more header bytes. Len = %d", this, mFlatHttpRequestHeaders.Length())); *countUsed = avail; return NS_OK; } // We have recvd all the headers, trim the local // buffer of the final empty line, and set countUsed to reflect // the whole header has been consumed. PRUint32 oldLen = mFlatHttpRequestHeaders.Length(); mFlatHttpRequestHeaders.SetLength(endHeader + 2); *countUsed = avail - (oldLen - endHeader) + 4; mSynFrameComplete = 1; // It is now OK to assign a streamID that we are assured will // be monotonically increasing amongst syn-streams on this // session mStreamID = mSession->RegisterStreamID(this); NS_ABORT_IF_FALSE(mStreamID & 1, "Spdy Stream Channel ID must be odd"); if (mStreamID >= 0x80000000) { // streamID must fit in 31 bits. This is theoretically possible // because stream ID assignment is asynchronous to stream creation // because of the protocol requirement that the ID in syn-stream // be monotonically increasing. In reality this is really not possible // because new streams stop being added to a session with 0x10000000 / 2 // IDs still available and no race condition is going to bridge that gap, // so we can be comfortable on just erroring out for correctness in that // case. LOG3(("Stream assigned out of range ID: 0x%X", mStreamID)); return NS_ERROR_UNEXPECTED; } // Now we need to convert the flat http headers into a set // of SPDY headers.. writing to mTxInlineFrame{sz} mTxInlineFrame[0] = SpdySession::kFlag_Control; mTxInlineFrame[1] = 2; /* version */ mTxInlineFrame[2] = 0; mTxInlineFrame[3] = SpdySession::CONTROL_TYPE_SYN_STREAM; // 4 to 7 are length and flags, we'll fill that in later PRUint32 networkOrderID = PR_htonl(mStreamID); memcpy(mTxInlineFrame + 8, &networkOrderID, 4); // this is the associated-to field, which is not used sending // from the client in the http binding memset (mTxInlineFrame + 12, 0, 4); // Priority flags are the C0 mask of byte 16. // From low to high: 00 40 80 C0 // higher raw priority values are actually less important // // The other 6 bits of 16 are unused. Spdy/3 will expand // priority to 4 bits. // // When Spdy/3 implements WINDOW_UPDATE the lowest priority // streams over a threshold (32?) should be given tiny // receive windows, separate from their spdy priority // if (mPriority >= nsISupportsPriority::PRIORITY_LOW) mTxInlineFrame[16] = SpdySession::kPri00; else if (mPriority >= nsISupportsPriority::PRIORITY_NORMAL) mTxInlineFrame[16] = SpdySession::kPri01; else if (mPriority >= nsISupportsPriority::PRIORITY_HIGH) mTxInlineFrame[16] = SpdySession::kPri02; else mTxInlineFrame[16] = SpdySession::kPri03; mTxInlineFrame[17] = 0; /* unused */ // nsCString methodHeader; // mTransaction->RequestHead()->Method()->ToUTF8String(methodHeader); const char *methodHeader = mTransaction->RequestHead()->Method().get(); nsCString hostHeader; mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader); nsCString versionHeader; if (mTransaction->RequestHead()->Version() == NS_HTTP_VERSION_1_1) versionHeader = NS_LITERAL_CSTRING("HTTP/1.1"); else versionHeader = NS_LITERAL_CSTRING("HTTP/1.0"); nsClassHashtable<nsCStringHashKey, nsCString> hdrHash; // use mRequestHead() to get a sense of how big to make the hash, // even though we are parsing the actual text stream because // it is legit to append headers. hdrHash.Init(1 + (mTransaction->RequestHead()->Headers().Count() * 2)); const char *beginBuffer = mFlatHttpRequestHeaders.BeginReading(); // need to hash all the headers together to remove duplicates, special // headers, etc.. PRInt32 crlfIndex = mFlatHttpRequestHeaders.Find("\r\n"); while (true) { PRInt32 startIndex = crlfIndex + 2; crlfIndex = mFlatHttpRequestHeaders.Find("\r\n", false, startIndex); if (crlfIndex == -1) break; PRInt32 colonIndex = mFlatHttpRequestHeaders.Find(":", false, startIndex, crlfIndex - startIndex); if (colonIndex == -1) break; nsDependentCSubstring name = Substring(beginBuffer + startIndex, beginBuffer + colonIndex); // all header names are lower case in spdy ToLowerCase(name); if (name.Equals("method") || name.Equals("version") || name.Equals("scheme") || name.Equals("keep-alive") || name.Equals("accept-encoding") || name.Equals("te") || name.Equals("connection") || name.Equals("proxy-connection") || name.Equals("url")) continue; nsCString *val = hdrHash.Get(name); if (!val) { val = new nsCString(); hdrHash.Put(name, val); } PRInt32 valueIndex = colonIndex + 1; while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ') ++valueIndex; nsDependentCSubstring v = Substring(beginBuffer + valueIndex, beginBuffer + crlfIndex); if (!val->IsEmpty()) val->Append(static_cast<char>(0)); val->Append(v); if (name.Equals("content-length")) { PRInt64 len; if (nsHttp::ParseInt64(val->get(), nsnull, &len)) mRequestBodyLen = len; } } mTxInlineFrameSize = 18; LOG3(("http request headers to encode are: \n%s", mFlatHttpRequestHeaders.get())); // The header block length PRUint16 count = hdrHash.Count() + 4; /* method, scheme, url, version */ CompressToFrame(count); // method, scheme, url, and version headers for request line CompressToFrame(NS_LITERAL_CSTRING("method")); CompressToFrame(methodHeader, strlen(methodHeader)); CompressToFrame(NS_LITERAL_CSTRING("scheme")); CompressToFrame(NS_LITERAL_CSTRING("https")); CompressToFrame(NS_LITERAL_CSTRING("url")); CompressToFrame(mTransaction->RequestHead()->RequestURI()); CompressToFrame(NS_LITERAL_CSTRING("version")); CompressToFrame(versionHeader); hdrHash.Enumerate(hdrHashEnumerate, this); CompressFlushFrame(); // 4 to 7 are length and flags, which we can now fill in (reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[1] = PR_htonl(mTxInlineFrameSize - 8); NS_ABORT_IF_FALSE(!mTxInlineFrame[4], "Size greater than 24 bits"); // For methods other than POST and PUT, we will set the fin bit // right on the syn stream packet. if (mTransaction->RequestHead()->Method() != nsHttp::Post && mTransaction->RequestHead()->Method() != nsHttp::Put) { mSentFinOnData = 1; mTxInlineFrame[4] = SpdySession::kFlag_Data_FIN; } Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, mTxInlineFrameSize - 18); // The size of the input headers is approximate PRUint32 ratio = (mTxInlineFrameSize - 18) * 100 / (11 + mTransaction->RequestHead()->RequestURI().Length() + mFlatHttpRequestHeaders.Length()); Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio); return NS_OK; }
void SpdyStream::CompressToFrame(const nsACString *str) { CompressToFrame(str->BeginReading(), str->Length()); }
void SpdyStream2::CompressToFrame(const nsACString &str) { CompressToFrame(str.BeginReading(), str.Length()); }