зеркало из https://github.com/mozilla/pjs.git
Partially fix 34743. Handle 100 (Continue) responses. Add an exclusion list
for servers with broken 1.1/pipelining. Fire onstop in case of the error on all queued up channels in the pipeline.
This commit is contained in:
Родитель
d2b46f5318
Коммит
39f7fc06c6
|
@ -47,6 +47,7 @@ interface nsIHTTPProtocolHandler : nsIProtocolHandler
|
|||
const unsigned long ALLOW_PROXY_KEEPALIVE = 4;
|
||||
const unsigned long ALLOW_PROXY_PIPELINING = 8;
|
||||
const unsigned long DONTRECORD_CAPABILITIES = 16;
|
||||
const unsigned long DONTALLOW_HTTP11 = 32;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -152,6 +153,12 @@ interface nsIHTTPProtocolHandler : nsIProtocolHandler
|
|||
|
||||
readonly attribute unsigned long capabilities;
|
||||
readonly attribute unsigned long keepAliveTimeout;
|
||||
|
||||
/**
|
||||
* Check for bad server and return the capabilites supported by
|
||||
* such server
|
||||
*/
|
||||
unsigned long Check4BrokenHTTPServers (in string serverHeader);
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
|
|
@ -1413,3 +1413,33 @@ nsHTTPHandler::ReleasePipelinedRequest (nsHTTPPipelinedRequest *pReq)
|
|||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static BrokenServersTable brokenServers_well_known [] =
|
||||
{
|
||||
{ "Netscape-Enterprise 3.6", nsHTTPHandler::BAD_SERVERS_MATCH_EXACT , 0}, // 3.6 but not 3.6 SPx - keep-alive is broken
|
||||
{ "Apache 1.2" , nsHTTPHandler::BAD_SERVERS_MATCH_ALL , nsIHTTPProtocolHandler::DONTALLOW_HTTP11} // chunk-encoding returns garbage sometimes
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTTPHandler::Check4BrokenHTTPServers (const char * a_Server, PRUint32 * a_Capabilities)
|
||||
{
|
||||
if (a_Capabilities == NULL)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
for (int i = 0; i < sizeof (brokenServers_well_known) / sizeof (BrokenServersTable); i++)
|
||||
{
|
||||
BrokenServersTable *tP = &brokenServers_well_known[i];
|
||||
if (tP -> matchFlags == BAD_SERVERS_MATCH_EXACT && !PL_strcmp (tP -> serverHeader, a_Server))
|
||||
{
|
||||
*a_Capabilities = tP -> capabilities;
|
||||
break;
|
||||
}
|
||||
else
|
||||
if (tP -> matchFlags == BAD_SERVERS_MATCH_ALL && !PL_strncmp (tP -> serverHeader, a_Server, strlen (tP -> serverHeader)))
|
||||
{
|
||||
*a_Capabilities = tP -> capabilities;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -65,6 +65,14 @@ class nsHTTPChannel;
|
|||
#define DEFAULT_HTTP_CONNECT_TIMEOUT 30
|
||||
#define DEFAULT_MAX_ALLOWED_KEEPALIVES 30
|
||||
|
||||
|
||||
typedef struct BrokenServersTable_s
|
||||
{
|
||||
const char *serverHeader;
|
||||
PRUint32 matchFlags;
|
||||
PRUint32 capabilities;
|
||||
} BrokenServersTable;
|
||||
|
||||
class nsHTTPPipelinedRequest;
|
||||
class nsIHTTPChannel;
|
||||
|
||||
|
@ -114,6 +122,10 @@ public:
|
|||
nsresult GetPipelinedRequest (nsIHTTPChannel* i_Channel, nsHTTPPipelinedRequest ** o_Req);
|
||||
nsresult ReleasePipelinedRequest (nsHTTPPipelinedRequest *pReq);
|
||||
|
||||
enum BrokenServerMatchFlags {
|
||||
BAD_SERVERS_MATCH_EXACT, BAD_SERVERS_MATCH_ALL
|
||||
};
|
||||
|
||||
protected:
|
||||
virtual ~nsHTTPHandler();
|
||||
nsresult InitUserAgentComponents();
|
||||
|
@ -167,7 +179,6 @@ private:
|
|||
|
||||
PRUint32 getCapabilities (const char *host, PRInt32 port, PRUint32 cap);
|
||||
void setCapabilities (nsIChannel* i_pTrans, PRUint32 aCapabilities);
|
||||
|
||||
};
|
||||
|
||||
#endif /* _nsHTTPHandler_h_ */
|
||||
|
|
|
@ -388,7 +388,7 @@ nsHTTPRequest::formHeaders (PRUint32 capabilities)
|
|||
|
||||
|
||||
nsresult
|
||||
nsHTTPRequest::formBuffer (nsCString * requestBuffer)
|
||||
nsHTTPRequest::formBuffer (nsCString * requestBuffer, PRUint32 capabilities)
|
||||
{
|
||||
nsXPIDLCString autoBuffer;
|
||||
nsresult rv;
|
||||
|
@ -431,7 +431,8 @@ nsHTTPRequest::formBuffer (nsCString * requestBuffer)
|
|||
httpVersion = " HTTP/0.9"CRLF;
|
||||
break;
|
||||
case HTTP_ONE_ONE:
|
||||
httpVersion = " HTTP/1.1"CRLF;
|
||||
if (! (capabilities & NS_STATIC_CAST (unsigned long, nsIHTTPProtocolHandler::DONTALLOW_HTTP11)) )
|
||||
httpVersion = " HTTP/1.1"CRLF;
|
||||
}
|
||||
|
||||
requestBuffer -> Append (httpVersion);
|
||||
|
@ -617,7 +618,7 @@ nsHTTPPipelinedRequest::WriteRequest ()
|
|||
for (index = mTotalWritten - mTotalProcessed; index < count; index++)
|
||||
{
|
||||
req = (nsHTTPRequest *) mRequests -> ElementAt (index);
|
||||
req -> formBuffer (&mRequestBuffer);
|
||||
req -> formBuffer (&mRequestBuffer, mCapabilities);
|
||||
if (index == 0)
|
||||
mTransport -> SetNotificationCallbacks (req -> mConnection);
|
||||
|
||||
|
@ -789,22 +790,29 @@ nsHTTPPipelinedRequest::OnStopRequest (nsIChannel* channel, nsISupports* i_Conte
|
|||
}
|
||||
}
|
||||
|
||||
// XXX/ruslan: we need to walk through all the requests !!!!!!!!!!!!!!
|
||||
|
||||
nsCOMPtr<nsIStreamListener> consumer;
|
||||
|
||||
req -> mConnection -> GetResponseDataListener (getter_AddRefs (consumer));
|
||||
if (consumer)
|
||||
req -> mConnection -> ResponseCompleted (consumer, rv, i_Msg);
|
||||
|
||||
// Notify the HTTPChannel that the request has finished
|
||||
|
||||
if (mTransport)
|
||||
while (req)
|
||||
{
|
||||
nsIChannel *p = mTransport;
|
||||
mTransport = null_nsCOMPtr ();
|
||||
nsCOMPtr<nsIStreamListener> consumer;
|
||||
req -> mConnection -> GetResponseDataListener (getter_AddRefs (consumer));
|
||||
|
||||
mHandler -> ReleaseTransport (p, nsIHTTPProtocolHandler::DONTRECORD_CAPABILITIES);
|
||||
if (consumer)
|
||||
req -> mConnection -> ResponseCompleted (consumer, rv, i_Msg);
|
||||
|
||||
// Notify the HTTPChannel that the request has finished
|
||||
|
||||
if (mTransport)
|
||||
{
|
||||
nsIChannel *p = mTransport;
|
||||
mTransport = null_nsCOMPtr ();
|
||||
|
||||
mHandler -> ReleaseTransport (p, nsIHTTPProtocolHandler::DONTRECORD_CAPABILITIES);
|
||||
}
|
||||
|
||||
NS_IF_RELEASE (req);
|
||||
req = nsnull;
|
||||
|
||||
if (NS_SUCCEEDED (AdvanceToNextRequest ()))
|
||||
GetCurrentRequest (&req);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ public:
|
|||
nsCOMPtr<nsIInputStream> mPostDataStream;
|
||||
|
||||
nsresult formHeaders (PRUint32 capabilities);
|
||||
nsresult formBuffer (nsCString * reqBuffer);
|
||||
nsresult formBuffer (nsCString * reqBuffer, PRUint32 capabilities);
|
||||
|
||||
nsHTTPPipelinedRequest* mPipelinedRequest;
|
||||
nsHTTPChannel* mConnection;
|
||||
|
|
|
@ -281,39 +281,45 @@ nsHTTPServerListener::OnDataAvailable(nsIChannel* channel,
|
|||
if (i_Length > 0)
|
||||
mDataReceived = PR_TRUE;
|
||||
|
||||
if (!mResponse)
|
||||
while (!mHeadersDone)
|
||||
{
|
||||
mResponse = new nsHTTPResponse ();
|
||||
if (!mResponse) {
|
||||
NS_ERROR("Failed to create the response object!");
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
if (!mResponse)
|
||||
{
|
||||
mResponse = new nsHTTPResponse ();
|
||||
if (!mResponse)
|
||||
{
|
||||
NS_ERROR("Failed to create the response object!");
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
NS_ADDREF (mResponse);
|
||||
mChannel -> SetResponse (mResponse);
|
||||
}
|
||||
NS_ADDREF(mResponse);
|
||||
mChannel->SetResponse(mResponse);
|
||||
}
|
||||
//
|
||||
// Parse the status line and the response headers from the server
|
||||
//
|
||||
if (!mHeadersDone) {
|
||||
|
||||
//
|
||||
// Parse the status line and the response headers from the server
|
||||
//
|
||||
|
||||
//
|
||||
// Parse the status line from the server. This is always the
|
||||
// first line of the response...
|
||||
//
|
||||
if (!mFirstLineParsed) {
|
||||
if (!mFirstLineParsed)
|
||||
{
|
||||
rv = ParseStatusLine(bufferInStream, i_Length, &actualBytesRead);
|
||||
NS_ASSERTION(i_Length - actualBytesRead <= i_Length, "wrap around");
|
||||
i_Length -= actualBytesRead;
|
||||
}
|
||||
|
||||
PR_LOG(gHTTPLog, PR_LOG_ALWAYS,
|
||||
("\tOnDataAvailable [this=%x]. Parsing Headers\n", this));
|
||||
PR_LOG (gHTTPLog, PR_LOG_ALWAYS,
|
||||
("\tOnDataAvailable [this=%x]. Parsing Headers\n", this));
|
||||
//
|
||||
// Parse the response headers as long as there is more data and
|
||||
// the headers are not done...
|
||||
//
|
||||
while (NS_SUCCEEDED(rv) && i_Length && !mHeadersDone) {
|
||||
while (NS_SUCCEEDED(rv) && i_Length && !mHeadersDone)
|
||||
{
|
||||
rv = ParseHTTPHeader(bufferInStream, i_Length, &actualBytesRead);
|
||||
NS_ASSERTION(i_Length - actualBytesRead <= i_Length, "wrap around");
|
||||
NS_ASSERTION (i_Length - actualBytesRead <= i_Length, "wrap around");
|
||||
i_Length -= actualBytesRead;
|
||||
}
|
||||
|
||||
|
@ -326,15 +332,18 @@ nsHTTPServerListener::OnDataAvailable(nsIChannel* channel,
|
|||
//
|
||||
// All the headers have been read.
|
||||
//
|
||||
rv = FinishedResponseHeaders ();
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (mResponse)
|
||||
{
|
||||
PRUint32 statusCode = 0;
|
||||
mResponse -> GetStatus (&statusCode);
|
||||
|
||||
if (statusCode == 304) // no content
|
||||
{
|
||||
rv = FinishedResponseHeaders ();
|
||||
if (NS_FAILED (rv))
|
||||
return rv;
|
||||
|
||||
rv = mPipelinedRequest -> AdvanceToNextRequest ();
|
||||
if (NS_FAILED (rv))
|
||||
{
|
||||
|
@ -352,8 +361,30 @@ nsHTTPServerListener::OnDataAvailable(nsIChannel* channel,
|
|||
OnStartRequest (nsnull, nsnull);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (statusCode == 100) // Continue
|
||||
{
|
||||
mHeadersDone = PR_FALSE;
|
||||
mFirstLineParsed = PR_FALSE;
|
||||
mHeaderBuffer.Truncate ();
|
||||
|
||||
mChannel -> SetResponse (nsnull);
|
||||
NS_RELEASE (mResponse);
|
||||
|
||||
mResponse = nsnull;
|
||||
mBytesReceived = 0;
|
||||
|
||||
PR_LOG (gHTTPLog, PR_LOG_DEBUG,
|
||||
("\tOnDataAvailable [this=%x]. (100) Continue\n", this));
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = FinishedResponseHeaders ();
|
||||
if (NS_FAILED (rv))
|
||||
return rv;
|
||||
}
|
||||
} /* mResponse */
|
||||
} /* while (!mHeadersDone) */
|
||||
|
||||
// At this point we've digested headers from the server and we're
|
||||
// onto the actual data. If this transaction was initiated without
|
||||
|
@ -455,17 +486,17 @@ nsHTTPServerListener::OnDataAvailable(nsIChannel* channel,
|
|||
if (mPipelinedRequest
|
||||
&& (cl != -1 && cl - mBodyBytesReceived == 0 || eof))
|
||||
{
|
||||
nsresult rv = mPipelinedRequest -> AdvanceToNextRequest ();
|
||||
nsresult rv1 = mPipelinedRequest -> AdvanceToNextRequest ();
|
||||
|
||||
if (NS_FAILED (rv))
|
||||
if (NS_FAILED (rv1))
|
||||
{
|
||||
mHandler -> ReleasePipelinedRequest (mPipelinedRequest);
|
||||
mPipelinedRequest = nsnull;
|
||||
|
||||
nsCOMPtr<nsISocketTransport> trans = do_QueryInterface (channel, &rv);
|
||||
nsCOMPtr<nsISocketTransport> trans = do_QueryInterface (channel, &rv1);
|
||||
|
||||
// XXX/ruslan: will be replaced with the new Cancel (code)
|
||||
if (NS_SUCCEEDED (rv))
|
||||
if (NS_SUCCEEDED (rv1))
|
||||
trans -> SetBytesExpected (0);
|
||||
|
||||
}
|
||||
|
@ -595,6 +626,7 @@ nsHTTPServerListener::OnStopRequest (nsIChannel* channel, nsISupports* i_pContex
|
|||
if (status != 304 || !mChannel -> mCachedResponse)
|
||||
{
|
||||
mChannel -> ResponseCompleted (mResponseDataListener, i_Status, i_pMsg);
|
||||
|
||||
mChannel -> mHTTPServerListener = 0;
|
||||
}
|
||||
|
||||
|
@ -620,17 +652,26 @@ nsHTTPServerListener::OnStopRequest (nsIChannel* channel, nsISupports* i_pContex
|
|||
if (ver == HTTP_ONE_ONE )
|
||||
{
|
||||
// ruslan: some older incorrect 1.1 servers may do this
|
||||
if (NS_SUCCEEDED (rv) && connectionHeader && !PL_strcmp (connectionHeader, "close" ))
|
||||
if (NS_SUCCEEDED (rv) && connectionHeader && !PL_strcasecmp (connectionHeader, "close" ))
|
||||
capabilities = 0;
|
||||
else
|
||||
{
|
||||
capabilities = (usingProxy ? nsIHTTPProtocolHandler::ALLOW_PROXY_KEEPALIVE|nsIHTTPProtocolHandler::ALLOW_PROXY_PIPELINING : nsIHTTPProtocolHandler::ALLOW_KEEPALIVE|nsIHTTPProtocolHandler::ALLOW_PIPELINING);
|
||||
|
||||
nsXPIDLCString serverHeader;
|
||||
rv = mResponse -> GetHeader (nsHTTPAtoms::Server, getter_Copies (serverHeader));
|
||||
|
||||
if (NS_SUCCEEDED (rv))
|
||||
mHandler -> Check4BrokenHTTPServers (serverHeader, &capabilities);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (ver == HTTP_ONE_ZERO)
|
||||
{
|
||||
if (NS_SUCCEEDED (rv) && connectionHeader && !PL_strcmp (connectionHeader, "keep-alive"))
|
||||
if (NS_SUCCEEDED (rv) && connectionHeader && !PL_strcasecmp (connectionHeader, "keep-alive"))
|
||||
capabilities = (usingProxy ? NS_STATIC_CAST (unsigned long, nsIHTTPProtocolHandler::ALLOW_PROXY_KEEPALIVE) : NS_STATIC_CAST (unsigned long, nsIHTTPProtocolHandler::ALLOW_KEEPALIVE));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -654,7 +695,7 @@ nsHTTPServerListener::OnStopRequest (nsIChannel* channel, nsISupports* i_pContex
|
|||
NS_IF_RELEASE (mChannel );
|
||||
NS_IF_RELEASE (mResponse);
|
||||
|
||||
return rv;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Загрузка…
Ссылка в новой задаче