Merge again

This commit is contained in:
Ed Morley 2012-06-29 08:47:59 +01:00
commit 681b648ee2
56 changed files with 2231 additions and 1589 deletions

View File

@ -919,6 +919,10 @@ pref("network.websocket.max-connections", 200);
// (i.e. wss://) websockets.
pref("network.websocket.allowInsecureFromHTTPS", false);
// by default we delay websocket reconnects to same host/port if previous
// connection failed, per RFC 6455 section 7.2.3
pref("network.websocket.delay-failed-reconnects", true);
// </ws>
// Server-Sent Events

View File

@ -2,75 +2,74 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_net_AutoClose_h
#define mozilla_net_AutoClose_h
#include "nsCOMPtr.h"
namespace mozilla { namespace net {
// Like an nsAutoPtr for XPCOM streams (e.g. nsIAsyncInputStream) and other
// refcounted classes that need to have the Close() method called explicitly
// before they are destroyed.
template <typename T>
class AutoClose
{
public:
AutoClose() { }
~AutoClose(){
Close();
}
operator bool() const
{
return mPtr;
}
already_AddRefed<T> forget()
{
return mPtr.forget();
}
void takeOver(AutoClose<T> & rhs)
{
Close();
mPtr = rhs.mPtr.forget();
}
// assign from |do_QueryInterface(expr, &rv)|
void operator=(const nsQueryInterfaceWithError rhs)
{
Close();
mPtr = rhs;
}
void CloseAndRelease()
{
Close();
mPtr = nsnull;
}
T* operator->() const
{
return mPtr.operator->();
}
private:
void Close()
{
if (mPtr) {
mPtr->Close();
}
}
void operator=(const AutoClose<T> &) MOZ_DELETE;
AutoClose(const AutoClose<T> &) MOZ_DELETE;
nsCOMPtr<T> mPtr;
};
} } // namespace mozilla::net
#endif // mozilla_net_AutoClose_h
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_net_AutoClose_h
#define mozilla_net_AutoClose_h
#include "nsCOMPtr.h"
namespace mozilla { namespace net {
// Like an nsAutoPtr for XPCOM streams (e.g. nsIAsyncInputStream) and other
// refcounted classes that need to have the Close() method called explicitly
// before they are destroyed.
template <typename T>
class AutoClose
{
public:
AutoClose() { }
~AutoClose(){
Close();
}
operator bool() const
{
return mPtr;
}
already_AddRefed<T> forget()
{
return mPtr.forget();
}
void takeOver(nsCOMPtr<T> & rhs)
{
Close();
mPtr = rhs.forget();
}
void takeOver(AutoClose<T> & rhs)
{
Close();
mPtr = rhs.mPtr.forget();
}
void CloseAndRelease()
{
Close();
mPtr = nsnull;
}
T* operator->() const
{
return mPtr.operator->();
}
private:
void Close()
{
if (mPtr) {
mPtr->Close();
}
}
void operator=(const AutoClose<T> &) MOZ_DELETE;
AutoClose(const AutoClose<T> &) MOZ_DELETE;
nsCOMPtr<T> mPtr;
};
} } // namespace mozilla::net
#endif // mozilla_net_AutoClose_h

View File

@ -239,7 +239,7 @@ private:
bool ResponseWouldVary() const;
bool MustValidateBasedOnQueryUrl() const;
nsresult SetupByteRangeRequest(PRUint32 partialLen);
nsresult StartBufferingCachedEntity();
nsresult OpenCacheInputStream(bool startBuffering);
nsCOMPtr<nsICacheListener> mChannel;
const bool mHasQueryString;
@ -266,7 +266,7 @@ private:
friend class nsHttpChannel;
/*in/out*/ nsHttpRequestHead mRequestHead;
/*in/out*/ nsAutoPtr<nsTArray<nsCString> > mRedirectedCachekeys;
/*out*/ AutoClose<nsIAsyncInputStream> mCacheAsyncInputStream;
/*out*/ AutoClose<nsIInputStream> mCacheInputStream;
/*out*/ nsAutoPtr<nsHttpResponseHead> mCachedResponseHead;
/*out*/ nsCOMPtr<nsISupports> mCachedSecurityInfo;
/*out*/ bool mCachedContentIsValid;
@ -290,6 +290,8 @@ nsHttpChannel::nsHttpChannel()
, mPostID(0)
, mRequestTime(0)
, mOnCacheEntryAvailableCallback(nsnull)
, mOfflineCacheAccess(0)
, mOfflineCacheLastModifiedTime(0)
, mCachedContentIsValid(false)
, mCachedContentIsPartial(false)
, mTransactionReplaced(false)
@ -973,9 +975,12 @@ nsHttpChannel::CallOnStartRequest()
LOG(("writing to the offline cache"));
rv = InitOfflineCacheEntry();
if (NS_FAILED(rv)) return rv;
rv = InstallOfflineCacheListener();
if (NS_FAILED(rv)) return rv;
// InitOfflineCacheEntry may have closed mOfflineCacheEntry
if (mOfflineCacheEntry) {
rv = InstallOfflineCacheListener();
if (NS_FAILED(rv)) return rv;
}
} else if (mCacheForOfflineUse) {
LOG(("offline cache is up to date, not updating"));
CloseOfflineCacheEntry();
@ -1176,7 +1181,7 @@ nsHttpChannel::ProcessResponse()
MOZ_ASSERT(!mCachedContentIsValid);
if (httpStatus != 304 && httpStatus != 206) {
mCacheAsyncInputStream.CloseAndRelease();
mCacheInputStream.CloseAndRelease();
}
// notify "http-on-examine-response" observers
@ -1225,7 +1230,7 @@ nsHttpChannel::ProcessResponse()
if (mCachedContentIsPartial) // an internal byte range request...
rv = ProcessPartialContent();
else {
mCacheAsyncInputStream.CloseAndRelease();
mCacheInputStream.CloseAndRelease();
rv = ProcessNormal();
}
break;
@ -1260,7 +1265,7 @@ nsHttpChannel::ProcessResponse()
rv = ProcessNotModified();
if (NS_FAILED(rv)) {
LOG(("ProcessNotModified failed [rv=%x]\n", rv));
mCacheAsyncInputStream.CloseAndRelease();
mCacheInputStream.CloseAndRelease();
rv = ProcessNormal();
}
else {
@ -2135,8 +2140,15 @@ nsHttpChannel::ProcessNotModified()
return NS_ERROR_FAILURE;
}
NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED);
if (!mDidReval) {
LOG(("Server returned a 304 response even though we did not send a "
"conditional request"));
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(mCachedResponseHead);
MOZ_ASSERT(mCacheEntry);
NS_ENSURE_TRUE(mCachedResponseHead && mCacheEntry, NS_ERROR_UNEXPECTED);
// If the 304 response contains a Last-Modified different than the
// one in our cache that is pretty suspicious and is, in at least the
@ -2655,6 +2667,9 @@ nsHttpChannel::OnOfflineCacheEntryForWritingAvailable(
if (NS_SUCCEEDED(aEntryStatus)) {
mOfflineCacheEntry = aEntry;
mOfflineCacheAccess = aAccess;
if (NS_FAILED(aEntry->GetLastModified(&mOfflineCacheLastModifiedTime))) {
mOfflineCacheLastModifiedTime = 0;
}
}
if (aEntryStatus == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
@ -2907,7 +2922,16 @@ HttpCacheQuery::CheckCache()
LOG(("HttpCacheQuery::CheckCache enter [channel=%p entry=%p access=%d]",
mChannel.get(), mCacheEntry.get(), mCacheAccess));
// Remember the request is a custom conditional request so that we can
// process any 304 response correctly.
mCustomConditionalRequest =
mRequestHead.PeekHeader(nsHttp::If_Modified_Since) ||
mRequestHead.PeekHeader(nsHttp::If_None_Match) ||
mRequestHead.PeekHeader(nsHttp::If_Unmodified_Since) ||
mRequestHead.PeekHeader(nsHttp::If_Match) ||
mRequestHead.PeekHeader(nsHttp::If_Range);
// Be pessimistic: assume the cache entry has no useful data.
mCachedContentIsValid = false;
@ -2966,7 +2990,7 @@ HttpCacheQuery::CheckCache()
(mCacheAccess == nsICache::ACCESS_READ &&
!(mLoadFlags & nsIRequest::INHIBIT_CACHING)) ||
mFallbackChannel)) {
rv = StartBufferingCachedEntity();
rv = OpenCacheInputStream(true);
if (NS_SUCCEEDED(rv)) {
mCachedContentIsValid = true;
// XXX: Isn't the cache entry already valid?
@ -2975,13 +2999,6 @@ HttpCacheQuery::CheckCache()
return rv;
}
mCustomConditionalRequest =
mRequestHead.PeekHeader(nsHttp::If_Modified_Since) ||
mRequestHead.PeekHeader(nsHttp::If_None_Match) ||
mRequestHead.PeekHeader(nsHttp::If_Unmodified_Since) ||
mRequestHead.PeekHeader(nsHttp::If_Match) ||
mRequestHead.PeekHeader(nsHttp::If_Range);
if (method != nsHttp::Head && !isCachedRedirect) {
// If the cached content-length is set and it does not match the data
// size of the cached content, then the cached response is partial...
@ -3015,7 +3032,7 @@ HttpCacheQuery::CheckCache()
rv = SetupByteRangeRequest(size);
mCachedContentIsPartial = NS_SUCCEEDED(rv);
if (mCachedContentIsPartial) {
rv = StartBufferingCachedEntity();
rv = OpenCacheInputStream(false);
} else {
// Make the request unconditional again.
mRequestHead.ClearHeader(nsHttp::Range);
@ -3193,13 +3210,8 @@ HttpCacheQuery::CheckCache()
}
}
// If there's any possibility that we may use the cached response's entity
// then start reading it into memory now. If we have to revalidate the
// entry and the revalidation fails, this will be a wasted effort, but
// it is much more likely that either we don't need to revalidate the entry
// or the entry will successfully revalidate.
if (mCachedContentIsValid || mDidReval) {
rv = StartBufferingCachedEntity();
rv = OpenCacheInputStream(mCachedContentIsValid);
if (NS_FAILED(rv)) {
// If we can't get the entity then we have to act as though we
// don't have the cache entry.
@ -3287,13 +3299,11 @@ nsHttpChannel::ShouldUpdateOfflineCacheEntry()
return true;
}
PRUint32 offlineLastModifiedTime;
rv = mOfflineCacheEntry->GetLastModified(&offlineLastModifiedTime);
if (NS_FAILED(rv)) {
if (mOfflineCacheLastModifiedTime == 0) {
return false;
}
if (docLastModifiedTime > offlineLastModifiedTime) {
if (docLastModifiedTime > mOfflineCacheLastModifiedTime) {
return true;
}
@ -3301,7 +3311,7 @@ nsHttpChannel::ShouldUpdateOfflineCacheEntry()
}
nsresult
HttpCacheQuery::StartBufferingCachedEntity()
HttpCacheQuery::OpenCacheInputStream(bool startBuffering)
{
AssertOnCacheThread();
@ -3354,22 +3364,39 @@ HttpCacheQuery::StartBufferingCachedEntity()
"load flag\n"));
}
// Open an input stream for the entity, but DO NOT create/connect
// mCachePump; that is done only when we decide to actually read the
// cached entity. By opening the input stream here, we allow the stream
// transport service to start reading the entity on one of its
// background threads while we do validation (if any).
nsCOMPtr<nsIInputStream> wrapper;
// Open an input stream for the entity, so that the call to OpenInputStream
// happens off the main thread.
nsCOMPtr<nsIInputStream> stream;
rv = mCacheEntry->OpenInputStream(0, getter_AddRefs(stream));
if (NS_FAILED(rv)) {
LOG(("Failed to open cache input stream [channel=%p, "
"mCacheEntry=%p]", mChannel.get(), mCacheEntry.get()));
return rv;
}
if (!startBuffering) {
// We do not connect the stream to the stream transport service if we
// have to validate the entry with the server. If we did, we would get
// into a race condition between the stream transport service reading
// the existing contents and the opening of the cache entry's output
// stream to write the new contents in the case where we get a non-304
// response.
LOG(("Opened cache input stream without buffering [channel=%p, "
"mCacheEntry=%p, stream=%p]", mChannel.get(),
mCacheEntry.get(), stream.get()));
mCacheInputStream.takeOver(stream);
return rv;
}
// Have the stream transport service start reading the entity on one of its
// background threads.
nsCOMPtr<nsITransport> transport;
nsCOMPtr<nsIInputStream> wrapper;
nsCOMPtr<nsIStreamTransportService> sts =
do_GetService(kStreamTransportServiceCID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = mCacheEntry->OpenInputStream(0, getter_AddRefs(stream));
if (NS_SUCCEEDED(rv)) {
rv = sts->CreateInputTransport(stream, PRInt64(-1), PRInt64(-1),
true, getter_AddRefs(transport));
@ -3377,9 +3404,6 @@ HttpCacheQuery::StartBufferingCachedEntity()
if (NS_SUCCEEDED(rv)) {
rv = transport->OpenInputStream(0, 0, 0, getter_AddRefs(wrapper));
}
if (NS_SUCCEEDED(rv)) {
mCacheAsyncInputStream = do_QueryInterface(wrapper, &rv);
}
if (NS_SUCCEEDED(rv)) {
LOG(("Opened cache input stream [channel=%p, wrapper=%p, "
"transport=%p, stream=%p]", this, wrapper.get(),
@ -3388,14 +3412,14 @@ HttpCacheQuery::StartBufferingCachedEntity()
LOG(("Failed to open cache input stream [channel=%p, "
"wrapper=%p, transport=%p, stream=%p]", this,
wrapper.get(), transport.get(), stream.get()));
if (wrapper)
wrapper->Close();
if (stream)
stream->Close();
stream->Close();
return rv;
}
return rv;
mCacheInputStream.takeOver(wrapper);
return NS_OK;
}
// Actually process the cached response that we started to handle in CheckCache
@ -3439,7 +3463,7 @@ nsHttpChannel::ReadFromCache(bool alreadyMarkedValid)
if (WillRedirect(mResponseHead)) {
// TODO: Bug 759040 - We should call HandleAsyncRedirect directly here,
// to avoid event dispatching latency.
MOZ_ASSERT(!mCacheAsyncInputStream);
MOZ_ASSERT(!mCacheInputStream);
LOG(("Skipping skip read of cached redirect entity\n"));
return AsyncCall(&nsHttpChannel::HandleAsyncRedirect);
}
@ -3448,7 +3472,7 @@ nsHttpChannel::ReadFromCache(bool alreadyMarkedValid)
if (!mCacheForOfflineUse) {
LOG(("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
"load flag\n"));
MOZ_ASSERT(!mCacheAsyncInputStream);
MOZ_ASSERT(!mCacheInputStream);
// TODO: Bug 759040 - We should call HandleAsyncNotModified directly
// here, to avoid event dispatching latency.
return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
@ -3457,22 +3481,22 @@ nsHttpChannel::ReadFromCache(bool alreadyMarkedValid)
if (!ShouldUpdateOfflineCacheEntry()) {
LOG(("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
"load flag (mCacheForOfflineUse case)\n"));
mCacheAsyncInputStream.CloseAndRelease();
mCacheInputStream.CloseAndRelease();
// TODO: Bug 759040 - We should call HandleAsyncNotModified directly
// here, to avoid event dispatching latency.
return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
}
}
MOZ_ASSERT(mCacheAsyncInputStream);
if (!mCacheAsyncInputStream) {
NS_ERROR("mCacheAsyncInputStream is null but we're expecting to "
MOZ_ASSERT(mCacheInputStream);
if (!mCacheInputStream) {
NS_ERROR("mCacheInputStream is null but we're expecting to "
"be able to read from it.");
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIAsyncInputStream> inputStream = mCacheAsyncInputStream.forget();
nsCOMPtr<nsIInputStream> inputStream = mCacheInputStream.forget();
rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump), inputStream,
PRInt64(-1), PRInt64(-1), 0, 0, true);
@ -3498,7 +3522,7 @@ void
nsHttpChannel::CloseCacheEntry(bool doomOnFailure)
{
mCacheQuery = nsnull;
mCacheAsyncInputStream.CloseAndRelease();
mCacheInputStream.CloseAndRelease();
if (!mCacheEntry)
return;
@ -4247,7 +4271,7 @@ nsHttpChannel::Cancel(nsresult status)
if (mTransactionPump)
mTransactionPump->Cancel(status);
mCacheQuery = nsnull;
mCacheAsyncInputStream.CloseAndRelease();
mCacheInputStream.CloseAndRelease();
if (mCachePump)
mCachePump->Cancel(status);
if (mAuthProvider)
@ -5436,7 +5460,7 @@ nsHttpChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
if (mCacheQuery) {
mRequestHead = mCacheQuery->mRequestHead;
mRedirectedCachekeys = mCacheQuery->mRedirectedCachekeys.forget();
mCacheAsyncInputStream.takeOver(mCacheQuery->mCacheAsyncInputStream);
mCacheInputStream.takeOver(mCacheQuery->mCacheInputStream);
mCachedResponseHead = mCacheQuery->mCachedResponseHead.forget();
mCachedSecurityInfo = mCacheQuery->mCachedSecurityInfo.forget();
mCachedContentIsValid = mCacheQuery->mCachedContentIsValid;
@ -5450,7 +5474,7 @@ nsHttpChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
// if the channel's already fired onStopRequest, then we should ignore
// this event.
if (!mIsPending) {
mCacheAsyncInputStream.CloseAndRelease();
mCacheInputStream.CloseAndRelease();
return NS_OK;
}

View File

@ -223,7 +223,6 @@ private:
nsresult UpdateExpirationTime();
nsresult CheckCache();
bool ShouldUpdateOfflineCacheEntry();
nsresult StartBufferingCachedEntity(bool usingSSL);
nsresult ReadFromCache(bool alreadyMarkedValid);
void CloseCacheEntry(bool doomOnFailure);
void CloseOfflineCacheEntry();
@ -294,8 +293,8 @@ private:
// cache specific data
nsRefPtr<HttpCacheQuery> mCacheQuery;
nsCOMPtr<nsICacheEntryDescriptor> mCacheEntry;
// We must close mCacheAsyncInputStream explicitly to avoid leaks.
AutoClose<nsIAsyncInputStream> mCacheAsyncInputStream;
// We must close mCacheInputStream explicitly to avoid leaks.
AutoClose<nsIInputStream> mCacheInputStream;
nsRefPtr<nsInputStreamPump> mCachePump;
nsAutoPtr<nsHttpResponseHead> mCachedResponseHead;
nsCOMPtr<nsISupports> mCachedSecurityInfo;
@ -310,6 +309,7 @@ private:
nsCOMPtr<nsICacheEntryDescriptor> mOfflineCacheEntry;
nsCacheAccessMode mOfflineCacheAccess;
PRUint32 mOfflineCacheLastModifiedTime;
nsCString mOfflineCacheClientID;
nsCOMPtr<nsIFile> mProfileDirectory;

View File

@ -34,6 +34,7 @@
#include "nsProxyRelease.h"
#include "nsNetUtil.h"
#include "mozilla/Attributes.h"
#include "TimeStamp.h"
#include "plbase64.h"
#include "prmem.h"
@ -41,8 +42,14 @@
#include "prbit.h"
#include "zlib.h"
// rather than slurp up all of nsIWebSocket.idl, which lives outside necko, just
// dupe one constant we need from it
#define CLOSE_GOING_AWAY 1001
extern PRThread *gSocketThread;
using namespace mozilla;
namespace mozilla {
namespace net {
@ -77,6 +84,403 @@ NS_IMPL_THREADSAFE_ISUPPORTS11(WebSocketChannel,
// some helper classes
//-----------------------------------------------------------------------------
// FailDelayManager
//
// Stores entries (searchable by {host, port}) of connections that have recently
// failed, so we can do delay of reconnects per RFC 6455 Section 7.2.3
//-----------------------------------------------------------------------------
// Initial reconnect delay is randomly chosen between 200-400 ms.
// This is a gentler backoff than the 0-5 seconds the spec offhandedly suggests.
const PRUint32 kWSReconnectInitialBaseDelay = 200;
const PRUint32 kWSReconnectInitialRandomDelay = 200;
// Base lifetime (in ms) of a FailDelay: kept longer if more failures occur
const PRUint32 kWSReconnectBaseLifeTime = 60 * 1000;
// Maximum reconnect delay (in ms)
const PRUint32 kWSReconnectMaxDelay = 60 * 1000;
// hold record of failed connections, and calculates needed delay for reconnects
// to same host/port.
class FailDelay
{
public:
FailDelay(nsCString address, PRInt32 port)
: mAddress(address), mPort(port)
{
mLastFailure = TimeStamp::Now();
mNextDelay = kWSReconnectInitialBaseDelay +
(rand() % kWSReconnectInitialRandomDelay);
}
// Called to update settings when connection fails again.
void FailedAgain()
{
mLastFailure = TimeStamp::Now();
// We use a truncated exponential backoff as suggested by RFC 6455,
// but multiply by 1.5 instead of 2 to be more gradual.
mNextDelay = PR_MIN(kWSReconnectMaxDelay, mNextDelay * 1.5);
LOG(("WebSocket: FailedAgain: host=%s, port=%d: incremented delay to %lu",
mAddress.get(), mPort, mNextDelay));
}
// returns 0 if there is no need to delay (i.e. delay interval is over)
PRUint32 RemainingDelay(TimeStamp rightNow)
{
TimeDuration dur = rightNow - mLastFailure;
PRUint32 sinceFail = (PRUint32) dur.ToMilliseconds();
if (sinceFail > mNextDelay)
return 0;
return mNextDelay - sinceFail;
}
bool IsExpired(TimeStamp rightNow)
{
return (mLastFailure +
TimeDuration::FromMilliseconds(kWSReconnectBaseLifeTime + mNextDelay))
<= rightNow;
}
nsCString mAddress; // IP address (or hostname if using proxy)
PRInt32 mPort;
private:
TimeStamp mLastFailure; // Time of last failed attempt
// mLastFailure + mNextDelay is the soonest we'll allow a reconnect
PRUint32 mNextDelay; // milliseconds
};
class FailDelayManager
{
public:
FailDelayManager()
{
MOZ_COUNT_CTOR(nsWSAdmissionManager);
mDelaysDisabled = false;
nsCOMPtr<nsIPrefBranch> prefService =
do_GetService(NS_PREFSERVICE_CONTRACTID);
bool boolpref = true;
nsresult rv;
rv = prefService->GetBoolPref("network.websocket.delay-failed-reconnects",
&boolpref);
if (NS_SUCCEEDED(rv) && !boolpref) {
mDelaysDisabled = true;
}
}
~FailDelayManager()
{
MOZ_COUNT_DTOR(nsWSAdmissionManager);
for (PRUint32 i = 0; i < mEntries.Length(); i++) {
delete mEntries[i];
}
}
void Add(nsCString &address, PRInt32 port)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
if (mDelaysDisabled)
return;
FailDelay *record = new FailDelay(address, port);
mEntries.AppendElement(record);
}
// Element returned may not be valid after next main thread event: don't keep
// pointer to it around
FailDelay* Lookup(nsCString &address, PRInt32 port,
PRUint32 *outIndex = nsnull)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
if (mDelaysDisabled)
return nsnull;
FailDelay *result = nsnull;
TimeStamp rightNow = TimeStamp::Now();
// We also remove expired entries during search: iterate from end to make
// indexing simpler
for (PRInt32 i = mEntries.Length() - 1; i >= 0; --i) {
FailDelay *fail = mEntries[i];
if (fail->mAddress.Equals(address) && fail->mPort == port) {
if (outIndex)
*outIndex = i;
result = fail;
} else if (fail->IsExpired(rightNow)) {
mEntries.RemoveElementAt(i);
delete fail;
}
}
return result;
}
// returns true if channel connects immediately, or false if it's delayed
bool DelayOrBegin(WebSocketChannel *ws)
{
if (!mDelaysDisabled) {
PRUint32 failIndex = 0;
FailDelay *fail = Lookup(ws->mAddress, ws->mPort, &failIndex);
if (fail) {
TimeStamp rightNow = TimeStamp::Now();
PRUint32 remainingDelay = fail->RemainingDelay(rightNow);
if (remainingDelay) {
// reconnecting within delay interval: delay by remaining time
nsresult rv;
ws->mReconnectDelayTimer =
do_CreateInstance("@mozilla.org/timer;1", &rv);
if (NS_SUCCEEDED(rv)) {
rv = ws->mReconnectDelayTimer->InitWithCallback(
ws, remainingDelay, nsITimer::TYPE_ONE_SHOT);
if (NS_SUCCEEDED(rv)) {
LOG(("WebSocket: delaying websocket [this=%p] by %lu ms",
ws, (unsigned long)remainingDelay));
ws->mConnecting = CONNECTING_DELAYED;
return false;
}
}
// if timer fails (which is very unlikely), drop down to BeginOpen call
} else if (fail->IsExpired(rightNow)) {
mEntries.RemoveElementAt(failIndex);
delete fail;
}
}
}
// Delays disabled, or no previous failure, or we're reconnecting after scheduled
// delay interval has passed: connect.
//
ws->mConnecting = CONNECTING_IN_PROGRESS;
// If BeginOpen fails, it calls AbortSession, which calls OnStopSession,
// which will ensure any remaining queued connection(s) are scheduled
return ws->BeginOpen();
}
// Remove() also deletes all expired entries as it iterates: better for
// battery life than using a periodic timer.
void Remove(nsCString &address, PRInt32 port)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
TimeStamp rightNow = TimeStamp::Now();
// iterate from end, to make deletion indexing easier
for (PRInt32 i = mEntries.Length() - 1; i >= 0; --i) {
FailDelay *entry = mEntries[i];
if ((entry->mAddress.Equals(address) && entry->mPort == port) ||
entry->IsExpired(rightNow)) {
mEntries.RemoveElementAt(i);
delete entry;
}
}
}
private:
nsTArray<FailDelay *> mEntries;
bool mDelaysDisabled;
};
//-----------------------------------------------------------------------------
// nsWSAdmissionManager
//
// 1) Ensures that only one websocket at a time is CONNECTING to a given IP
// address (or hostname, if using proxy), per RFC 6455 Section 4.1.
// 2) Delays reconnects to IP/host after connection failure, per Section 7.2.3
//-----------------------------------------------------------------------------
class nsWSAdmissionManager
{
public:
nsWSAdmissionManager() : mSessionCount(0)
{
MOZ_COUNT_CTOR(nsWSAdmissionManager);
}
class nsOpenConn
{
public:
nsOpenConn(nsCString &addr, WebSocketChannel *channel)
: mAddress(addr), mChannel(channel) { MOZ_COUNT_CTOR(nsOpenConn); }
~nsOpenConn() { MOZ_COUNT_DTOR(nsOpenConn); }
nsCString mAddress;
WebSocketChannel *mChannel;
};
~nsWSAdmissionManager()
{
MOZ_COUNT_DTOR(nsWSAdmissionManager);
for (PRUint32 i = 0; i < mQueue.Length(); i++)
delete mQueue[i];
}
// Determine if we will open connection immediately (returns true), or
// delay/queue the connection (returns false)
bool ConditionallyConnect(WebSocketChannel *ws)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
NS_ABORT_IF_FALSE(ws->mConnecting == NOT_CONNECTING, "opening state");
// If there is already another WS channel connecting to this IP address,
// defer BeginOpen and mark as waiting in queue.
bool found = (IndexOf(ws->mAddress) >= 0);
// Always add ourselves to queue, even if we'll connect immediately
nsOpenConn *newdata = new nsOpenConn(ws->mAddress, ws);
mQueue.AppendElement(newdata);
if (found) {
ws->mConnecting = CONNECTING_QUEUED;
return false;
}
return mFailures.DelayOrBegin(ws);
}
bool OnConnected(WebSocketChannel *aChannel)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
NS_ABORT_IF_FALSE(aChannel->mConnecting == CONNECTING_IN_PROGRESS,
"Channel completed connect, but not connecting?");
aChannel->mConnecting = NOT_CONNECTING;
// Remove from queue
RemoveFromQueue(aChannel);
// Connection succeeded, so stop keeping track of any previous failures
mFailures.Remove(aChannel->mAddress, aChannel->mPort);
// Check for queued connections to same host.
// Note: still need to check for failures, since next websocket with same
// host may have different port
return ConnectNext(aChannel->mAddress);
}
// Called every time a websocket channel ends its session (including going away
// w/o ever successfully creating a connection)
bool OnStopSession(WebSocketChannel *aChannel, nsresult aReason)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
if (NS_FAILED(aReason)) {
// Have we seen this failure before?
FailDelay *knownFailure = mFailures.Lookup(aChannel->mAddress,
aChannel->mPort);
if (knownFailure) {
// repeated failure to connect: increase delay for next connection
knownFailure->FailedAgain();
} else {
// new connection failure: record it.
LOG(("WebSocket: connection to %s, %d failed: [this=%p]",
aChannel->mAddress.get(), (int)aChannel->mPort, aChannel));
mFailures.Add(aChannel->mAddress, aChannel->mPort);
}
}
if (aChannel->mConnecting) {
// Only way a connecting channel may get here w/o failing is if it was
// closed with GOING_AWAY (1001) because of navigation, tab close, etc.
NS_ABORT_IF_FALSE(NS_FAILED(aReason) ||
aChannel->mScriptCloseCode == CLOSE_GOING_AWAY,
"websocket closed while connecting w/o failing?");
RemoveFromQueue(aChannel);
bool wasNotQueued = (aChannel->mConnecting != CONNECTING_QUEUED);
aChannel->mConnecting = NOT_CONNECTING;
if (wasNotQueued)
return ConnectNext(aChannel->mAddress);
}
return false;
}
bool ConnectNext(nsCString &hostName)
{
PRInt32 index = IndexOf(hostName);
if (index >= 0) {
WebSocketChannel *chan = mQueue[index]->mChannel;
NS_ABORT_IF_FALSE(chan->mConnecting == CONNECTING_QUEUED,
"transaction not queued but in queue");
LOG(("WebSocket: ConnectNext: found channel [this=%p] in queue", chan));
return mFailures.DelayOrBegin(chan);
}
return false;
}
void IncrementSessionCount()
{
PR_ATOMIC_INCREMENT(&mSessionCount);
}
void DecrementSessionCount()
{
PR_ATOMIC_DECREMENT(&mSessionCount);
}
PRInt32 SessionCount()
{
return mSessionCount;
}
private:
void RemoveFromQueue(WebSocketChannel *aChannel)
{
PRInt32 index = IndexOf(aChannel);
NS_ABORT_IF_FALSE(index >= 0, "connection to remove not in queue");
if (index >= 0) {
nsOpenConn *olddata = mQueue[index];
mQueue.RemoveElementAt(index);
delete olddata;
}
}
PRInt32 IndexOf(nsCString &aStr)
{
for (PRUint32 i = 0; i < mQueue.Length(); i++)
if (aStr == (mQueue[i])->mAddress)
return i;
return -1;
}
PRInt32 IndexOf(WebSocketChannel *aChannel)
{
for (PRUint32 i = 0; i < mQueue.Length(); i++)
if (aChannel == (mQueue[i])->mChannel)
return i;
return -1;
}
// SessionCount might be decremented from the main or the socket
// thread, so manage it with atomic counters
PRInt32 mSessionCount;
// Queue for websockets that have not completed connecting yet.
// The first nsOpenConn with a given address will be either be
// CONNECTING_IN_PROGRESS or CONNECTING_DELAYED. Later ones with the same
// hostname must be CONNECTING_QUEUED.
//
// We could hash hostnames instead of using a single big vector here, but the
// dataset is expected to be small.
nsTArray<nsOpenConn *> mQueue;
FailDelayManager mFailures;
};
static nsWSAdmissionManager *sWebSocketAdmissions = nsnull;
//-----------------------------------------------------------------------------
// CallOnMessageAvailable
//-----------------------------------------------------------------------------
@ -121,14 +525,18 @@ public:
NS_DECL_ISUPPORTS
CallOnStop(WebSocketChannel *aChannel,
nsresult aData)
nsresult aReason)
: mChannel(aChannel),
mData(aData) {}
mReason(aReason) {}
NS_IMETHOD Run()
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
mChannel->mListener->OnStop(mChannel->mContext, mData);
sWebSocketAdmissions->OnStopSession(mChannel, mReason);
if (mChannel->mListener)
mChannel->mListener->OnStop(mChannel->mContext, mReason);
return NS_OK;
}
@ -136,7 +544,7 @@ private:
~CallOnStop() {}
nsRefPtr<WebSocketChannel> mChannel;
nsresult mData;
nsresult mReason;
};
NS_IMPL_THREADSAFE_ISUPPORTS1(CallOnStop, nsIRunnable)
@ -370,176 +778,6 @@ private:
};
NS_IMPL_THREADSAFE_ISUPPORTS1(OutboundEnqueuer, nsIRunnable)
//-----------------------------------------------------------------------------
// nsWSAdmissionManager
//-----------------------------------------------------------------------------
// Section 4.1 requires that only a single websocket at a time can be CONNECTING
// to any given IP address (or hostname, if proxy doing DNS for us). This class
// ensures that we delay connecting until any pending connection for the same
// IP/addr is complete (i.e. until before the 101 upgrade complete response
// comes back and an 'open' javascript event is created)
class nsWSAdmissionManager
{
public:
nsWSAdmissionManager() : mSessionCount(0)
{
MOZ_COUNT_CTOR(nsWSAdmissionManager);
}
class nsOpenConn
{
public:
nsOpenConn(nsCString &addr, WebSocketChannel *channel)
: mAddress(addr), mChannel(channel) { MOZ_COUNT_CTOR(nsOpenConn); }
~nsOpenConn() { MOZ_COUNT_DTOR(nsOpenConn); }
nsCString mAddress;
WebSocketChannel *mChannel;
};
~nsWSAdmissionManager()
{
MOZ_COUNT_DTOR(nsWSAdmissionManager);
for (PRUint32 i = 0; i < mData.Length(); i++)
delete mData[i];
}
bool ConditionallyConnect(nsCString &aStr, WebSocketChannel *ws)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
// if aStr is not in mData then we return true, else false.
// in either case aStr is then added to mData - meaning
// there will be duplicates when this function has been
// called with the same parameter multiple times.
// we could hash this, but the dataset is expected to be
// small
// There may already be another WS channel connecting to this IP address, in
// which case we'll still create a new nsOpenConn but defer BeginOpen until
// that channel completes connecting.
bool found = (IndexOf(aStr) >= 0);
nsOpenConn *newdata = new nsOpenConn(aStr, ws);
mData.AppendElement(newdata);
NS_ABORT_IF_FALSE (!ws->mOpenRunning && !ws->mOpenBlocked,
"opening state");
if (!found) {
ws->mOpenRunning = 1;
ws->BeginOpen();
} else {
ws->mOpenBlocked = 1;
}
return !found;
}
bool Complete(WebSocketChannel *aChannel)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
NS_ABORT_IF_FALSE(!aChannel->mOpenBlocked,
"blocked, but complete nsOpenConn");
// It is possible this has already been canceled
if (!aChannel->mOpenRunning)
return false;
PRInt32 index = IndexOf(aChannel);
NS_ABORT_IF_FALSE(index >= 0, "completed connection not in open list");
aChannel->mOpenRunning = 0;
nsOpenConn *olddata = mData[index];
mData.RemoveElementAt(index);
delete olddata;
// are there more of the same address pending dispatch?
return ConnectNext(aChannel->mAddress);
}
bool Cancel(WebSocketChannel *aChannel)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
PRInt32 index = IndexOf(aChannel);
NS_ABORT_IF_FALSE(index >= 0, "Cancelled connection not in open list");
NS_ABORT_IF_FALSE(aChannel->mOpenRunning ^ aChannel->mOpenBlocked,
"cancel without running xor blocked");
bool wasRunning = aChannel->mOpenRunning;
aChannel->mOpenRunning = 0;
aChannel->mOpenBlocked = 0;
nsOpenConn *olddata = mData[index];
mData.RemoveElementAt(index);
delete olddata;
// if we are running we can run another one
if (wasRunning)
return ConnectNext(aChannel->mAddress);
return false;
}
bool ConnectNext(nsCString &hostName)
{
PRInt32 index = IndexOf(hostName);
if (index >= 0) {
WebSocketChannel *chan = mData[index]->mChannel;
NS_ABORT_IF_FALSE(chan->mOpenBlocked,
"transaction not blocked but in queue");
NS_ABORT_IF_FALSE(!chan->mOpenRunning, "transaction already running");
chan->mOpenBlocked = 0;
chan->mOpenRunning = 1;
chan->BeginOpen();
return true;
}
return false;
}
void IncrementSessionCount()
{
PR_ATOMIC_INCREMENT(&mSessionCount);
}
void DecrementSessionCount()
{
PR_ATOMIC_DECREMENT(&mSessionCount);
}
PRInt32 SessionCount()
{
return mSessionCount;
}
private:
nsTArray<nsOpenConn *> mData;
PRInt32 IndexOf(nsCString &aStr)
{
for (PRUint32 i = 0; i < mData.Length(); i++)
if (aStr == (mData[i])->mAddress)
return i;
return -1;
}
PRInt32 IndexOf(WebSocketChannel *aChannel)
{
for (PRUint32 i = 0; i < mData.Length(); i++)
if (aChannel == (mData[i])->mChannel)
return i;
return -1;
}
// SessionCount might be decremented from the main or the socket
// thread, so manage it with atomic counters
PRInt32 mSessionCount;
};
//-----------------------------------------------------------------------------
// nsWSCompression
//
@ -667,15 +905,15 @@ private:
PRUint8 mBuffer[kBufferLen];
};
static nsWSAdmissionManager *sWebSocketAdmissions = nsnull;
//-----------------------------------------------------------------------------
// WebSocketChannel
//-----------------------------------------------------------------------------
WebSocketChannel::WebSocketChannel() :
mPort(0),
mCloseTimeout(20000),
mOpenTimeout(20000),
mConnecting(NOT_CONNECTING),
mPingTimeout(0),
mPingResponseTimeout(10000),
mMaxConcurrentConnections(200),
@ -691,8 +929,6 @@ WebSocketChannel::WebSocketChannel() :
mAutoFollowRedirects(0),
mReleaseOnTransmit(0),
mTCPClosed(0),
mOpenBlocked(0),
mOpenRunning(0),
mChannelWasOpened(0),
mDataStarted(0),
mIncrementedSessionCount(0),
@ -727,7 +963,7 @@ WebSocketChannel::~WebSocketChannel()
// this stop is a nop if the normal connect/close is followed
StopSession(NS_ERROR_UNEXPECTED);
NS_ABORT_IF_FALSE(!mOpenRunning && !mOpenBlocked, "op");
NS_ABORT_IF_FALSE(mConnecting == NOT_CONNECTING, "op");
moz_free(mBuffer);
moz_free(mDynamicOutput);
@ -781,7 +1017,7 @@ WebSocketChannel::Shutdown()
sWebSocketAdmissions = nsnull;
}
nsresult
bool
WebSocketChannel::BeginOpen()
{
LOG(("WebSocketChannel::BeginOpen() %p\n", this));
@ -793,29 +1029,40 @@ WebSocketChannel::BeginOpen()
LOG(("WebSocketChannel::BeginOpen: Resuming Redirect\n"));
rv = mRedirectCallback->OnRedirectVerifyCallback(NS_OK);
mRedirectCallback = nsnull;
return rv;
return false;
}
nsCOMPtr<nsIChannel> localChannel = do_QueryInterface(mChannel, &rv);
if (NS_FAILED(rv)) {
LOG(("WebSocketChannel::BeginOpen: cannot async open\n"));
AbortSession(NS_ERROR_UNEXPECTED);
return rv;
return false;
}
rv = localChannel->AsyncOpen(this, mHttpChannel);
if (NS_FAILED(rv)) {
LOG(("WebSocketChannel::BeginOpen: cannot async open\n"));
AbortSession(NS_ERROR_CONNECTION_REFUSED);
return rv;
return false;
}
mChannelWasOpened = 1;
mOpenTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
if (NS_SUCCEEDED(rv))
mOpenTimer->InitWithCallback(this, mOpenTimeout, nsITimer::TYPE_ONE_SHOT);
if (NS_FAILED(rv)) {
LOG(("WebSocketChannel::BeginOpen: cannot create open timer\n"));
AbortSession(NS_ERROR_UNEXPECTED);
return false;
}
return rv;
rv = mOpenTimer->InitWithCallback(this, mOpenTimeout,
nsITimer::TYPE_ONE_SHOT);
if (NS_FAILED(rv)) {
LOG(("WebSocketChannel::BeginOpen: cannot initialize open timer\n"));
AbortSession(NS_ERROR_UNEXPECTED);
return false;
}
return true;
}
bool
@ -1578,9 +1825,6 @@ WebSocketChannel::StopSession(nsresult reason)
mCallbacks = nsnull;
}
if (mOpenRunning || mOpenBlocked)
sWebSocketAdmissions->Cancel(this);
if (mCloseTimer) {
mCloseTimer->Cancel();
mCloseTimer = nsnull;
@ -1591,6 +1835,11 @@ WebSocketChannel::StopSession(nsresult reason)
mOpenTimer = nsnull;
}
if (mReconnectDelayTimer) {
mReconnectDelayTimer->Cancel();
mReconnectDelayTimer = nsnull;
}
if (mPingTimer) {
mPingTimer->Cancel();
mPingTimer = nsnull;
@ -1657,8 +1906,7 @@ WebSocketChannel::StopSession(nsresult reason)
if (!mCalledOnStop) {
mCalledOnStop = 1;
if (mListener)
NS_DispatchToMainThread(new CallOnStop(this, reason));
NS_DispatchToMainThread(new CallOnStop(this, reason));
}
return;
@ -1893,6 +2141,10 @@ WebSocketChannel::ApplyForAdmission()
rv = mURI->GetHost(hostName);
NS_ENSURE_SUCCESS(rv, rv);
mAddress = hostName;
rv = mURI->GetPort(&mPort);
NS_ENSURE_SUCCESS(rv, rv);
if (mPort == -1)
mPort = (mEncrypted ? kDefaultWSSPort : kDefaultWSPort);
// expect the callback in ::OnLookupComplete
LOG(("WebSocketChannel::ApplyForAdmission: checking for concurrent open\n"));
@ -1913,7 +2165,12 @@ WebSocketChannel::StartWebsocketData()
LOG(("WebSocketChannel::StartWebsocketData() %p", this));
NS_ABORT_IF_FALSE(!mDataStarted, "StartWebsocketData twice");
mDataStarted = 1;
// We're now done CONNECTING, which means we can now open another,
// perhaps parallel, connection to the same host if one
// is pending
sWebSocketAdmissions->OnConnected(this);
LOG(("WebSocketChannel::StartWebsocketData Notifying Listener %p\n",
mListener.get()));
@ -1953,7 +2210,7 @@ WebSocketChannel::OnLookupComplete(nsICancelable *aRequest,
LOG(("WebSocketChannel::OnLookupComplete: Failed GetNextAddr\n"));
}
if (sWebSocketAdmissions->ConditionallyConnect(mAddress, this)) {
if (sWebSocketAdmissions->ConditionallyConnect(this)) {
LOG(("WebSocketChannel::OnLookupComplete: Proceeding with Open\n"));
} else {
LOG(("WebSocketChannel::OnLookupComplete: Deferring Open\n"));
@ -2077,17 +2334,19 @@ WebSocketChannel::AsyncOnChannelRedirect(
return rv;
}
// We cannot just tell the callback OK right now due to the 1 connect at a
// time policy. First we need to complete the old location and then start the
// lookup chain for the new location - once that is complete and we have been
// admitted, OnRedirectVerifyCallback(NS_OK) will be called out of BeginOpen()
sWebSocketAdmissions->Complete(this);
mAddress.Truncate();
// Redirected-to URI may need to be delayed by 1-connecting-per-host and
// delay-after-fail algorithms. So hold off calling OnRedirectVerifyCallback
// until BeginOpen, when we know it's OK to proceed with new channel.
mRedirectCallback = callback;
mChannelWasOpened = 0;
// Mark old channel as successfully connected so we'll clear any FailDelay
// associated with the old URI. Note: no need to also call OnStopSession:
// it's a no-op for successful, already-connected channels.
sWebSocketAdmissions->OnConnected(this);
// ApplyForAdmission as if we were starting from fresh...
mAddress.Truncate();
mChannelWasOpened = 0;
rv = ApplyForAdmission();
if (NS_FAILED(rv)) {
LOG(("WebSocketChannel: Redirect failed due to DNS failure\n"));
@ -2127,6 +2386,16 @@ WebSocketChannel::Notify(nsITimer *timer)
return NS_OK;
AbortSession(NS_ERROR_NET_TIMEOUT);
} else if (timer == mReconnectDelayTimer) {
NS_ABORT_IF_FALSE(mConnecting == CONNECTING_DELAYED,
"woke up from delay w/o being delayed?");
mReconnectDelayTimer = nsnull;
LOG(("WebSocketChannel: connecting [this=%p] after reconnect delay", this));
// - if BeginOpen fails, it calls AbortSession, which calls OnStopSession,
// which will ensure any remaining queued connection(s) are scheduled
mConnecting = CONNECTING_IN_PROGRESS;
this->BeginOpen();
} else if (timer == mPingTimer) {
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
"not socket thread");
@ -2351,12 +2620,6 @@ WebSocketChannel::Close(PRUint16 code, const nsACString & reason)
return NS_OK;
}
if (!mTransport) {
LOG(("WebSocketChannel::Close() without transport - aborting."));
AbortSession(NS_ERROR_NOT_CONNECTED);
return NS_ERROR_NOT_CONNECTED;
}
// The API requires the UTF-8 string to be 123 or less bytes
if (reason.Length() > 123)
return NS_ERROR_ILLEGAL_VALUE;
@ -2365,6 +2628,20 @@ WebSocketChannel::Close(PRUint16 code, const nsACString & reason)
mScriptCloseReason = reason;
mScriptCloseCode = code;
if (!mTransport) {
nsresult rv;
if (code == CLOSE_GOING_AWAY) {
// Not an error: for example, tab has closed or navigated away
LOG(("WebSocketChannel::Close() GOING_AWAY without transport."));
rv = NS_OK;
} else {
LOG(("WebSocketChannel::Close() without transport - error."));
rv = NS_ERROR_NOT_CONNECTED;
}
StopSession(rv);
return rv;
}
return mSocketThread->Dispatch(
new OutboundEnqueuer(this, new OutboundMessage(kMsgTypeFin, nsnull)),
nsIEventTarget::DISPATCH_NORMAL);
@ -2470,16 +2747,6 @@ WebSocketChannel::OnStartRequest(nsIRequest *aRequest,
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
NS_ABORT_IF_FALSE(!mRecvdHttpOnStartRequest, "OTA duplicated");
// Generating the onStart event will take us out of the
// CONNECTING state which means we can now open another,
// perhaps parallel, connection to the same host if one
// is pending
if (sWebSocketAdmissions->Complete(this))
LOG(("WebSocketChannel::OnStartRequest: Starting Pending Open\n"));
else
LOG(("WebSocketChannel::OnStartRequest: No More Pending Opens\n"));
if (mOpenTimer) {
mOpenTimer->Cancel();
mOpenTimer = nsnull;

View File

@ -42,6 +42,14 @@ class CallOnStop;
class CallOnServerClose;
class CallAcknowledge;
// Used to enforce "1 connecting websocket per host" rule, and reconnect delays
enum wsConnectingState {
NOT_CONNECTING = 0, // Not yet (or no longer) trying to open connection
CONNECTING_QUEUED, // Waiting for other ws to same host to finish opening
CONNECTING_DELAYED, // Delayed by "reconnect after failure" algorithm
CONNECTING_IN_PROGRESS // Started connection: waiting for result
};
class WebSocketChannel : public BaseWebSocketChannel,
public nsIHttpUpgradeListener,
public nsIStreamListener,
@ -101,6 +109,7 @@ protected:
private:
friend class OutboundEnqueuer;
friend class nsWSAdmissionManager;
friend class FailDelayManager;
friend class CallOnMessageAvailable;
friend class CallOnStop;
friend class CallOnServerClose;
@ -117,7 +126,7 @@ private:
void GeneratePong(PRUint8 *payload, PRUint32 len);
void GeneratePing();
nsresult BeginOpen();
bool BeginOpen();
nsresult HandleExtensions();
nsresult SetupRequest();
nsresult ApplyForAdmission();
@ -149,7 +158,11 @@ private:
nsCOMPtr<nsIRandomGenerator> mRandomGenerator;
nsCString mHashedSecret;
// Used as key for connection managment: Initially set to hostname from URI,
// then to IP address (unless we're leaving DNS resolution to a proxy server)
nsCString mAddress;
PRInt32 mPort; // WS server port
nsCOMPtr<nsISocketTransport> mTransport;
nsCOMPtr<nsIAsyncInputStream> mSocketIn;
@ -160,6 +173,8 @@ private:
nsCOMPtr<nsITimer> mOpenTimer;
PRUint32 mOpenTimeout; /* milliseconds */
wsConnectingState mConnecting; /* 0 if not connecting */
nsCOMPtr<nsITimer> mReconnectDelayTimer;
nsCOMPtr<nsITimer> mPingTimer;
PRUint32 mPingTimeout; /* milliseconds */
@ -183,8 +198,6 @@ private:
PRUint32 mAutoFollowRedirects : 1;
PRUint32 mReleaseOnTransmit : 1;
PRUint32 mTCPClosed : 1;
PRUint32 mOpenBlocked : 1;
PRUint32 mOpenRunning : 1;
PRUint32 mChannelWasOpened : 1;
PRUint32 mDataStarted : 1;
PRUint32 mIncrementedSessionCount : 1;

View File

@ -0,0 +1,46 @@
var _CSvc;
function get_cache_service() {
if (_CSvc)
return _CSvc;
return _CSvc = Components.classes["@mozilla.org/network/cache-service;1"]
.getService(Components.interfaces.nsICacheService);
}
function evict_cache_entries(where)
{
if (where == null)
where = Components.interfaces.nsICache.STORE_ANYWHERE;
get_cache_service().evictEntries(where);
}
function asyncOpenCacheEntry(key, sessionName, storagePolicy, access, callback)
{
function CacheListener() { }
CacheListener.prototype = {
QueryInterface: function (iid) {
if (iid.equals(Components.interfaces.nsICacheListener) ||
iid.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
onCacheEntryAvailable: function (entry, access, status) {
callback(status, entry);
},
run: function () {
var cache = get_cache_service();
var session = cache.createSession(
sessionName,
storagePolicy,
Components.interfaces.nsICache.STREAM_BASED);
var cacheEntry = session.asyncOpenCacheEntry(key, access, this);
}
};
(new CacheListener()).run();
}

View File

@ -0,0 +1,86 @@
"use strict";
// https://bugzilla.mozilla.org/show_bug.cgi?id=761228
do_load_httpd_js();
var httpServer = null;
const testFileName = "test_customConditionalRequest_304";
const basePath = "/" + testFileName + "/";
const baseURI = "http://localhost:4444" + basePath;
const unexpected304 = "unexpected304";
const existingCached304 = "existingCached304";
function make_uri(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newURI(url, null, null);
}
function make_channel(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var chan = ios.newChannel(url, null, null).QueryInterface(Ci.nsIHttpChannel);
return chan;
}
function clearCache() {
var service = Components.classes["@mozilla.org/network/cache-service;1"]
.getService(Ci.nsICacheService);
service.evictEntries(Ci.nsICache.STORE_ANYWHERE);
}
function alwaysReturn304Handler(metadata, response) {
response.setStatusLine(metadata.httpVersion, 304, "Not Modified");
response.setHeader("Returned-From-Handler", "1");
}
function run_test() {
evict_cache_entries();
httpServer = new nsHttpServer();
httpServer.registerPathHandler(basePath + unexpected304,
alwaysReturn304Handler);
httpServer.registerPathHandler(basePath + existingCached304,
alwaysReturn304Handler);
httpServer.start(4444);
run_next_test();
}
function finish_test(request, buffer) {
httpServer.stop(do_test_finished);
}
function consume304(request, buffer) {
do_check_eq(request.responseStatus, 304);
do_check_eq(request.getResponseHeader("Returned-From-Handler"), "1");
run_next_test();
}
// Test that we return a 304 response to the caller when we are not expecting
// a 304 response (i.e. when the server shouldn't have sent us one).
add_test(function test_unexpected_304() {
var chan = make_channel(baseURI + unexpected304);
chan.asyncOpen(new ChannelListener(consume304, null), null);
});
// Test that we can cope with a 304 response that was (erroneously) stored in
// the cache.
add_test(function test_304_stored_in_cache() {
asyncOpenCacheEntry(
baseURI + existingCached304, "HTTP",
Ci.nsICache.STORE_ANYWHERE, Ci.nsICache.ACCESS_READ_WRITE,
function (entryStatus, cacheEntry) {
cacheEntry.setMetaDataElement("request-method", "GET");
cacheEntry.setMetaDataElement("response-head",
"HTTP/1.1 304 Not Modified\r\n" +
"\r\n");
cacheEntry.close();
var chan = make_channel(baseURI + existingCached304);
// make it a custom conditional request
chan.QueryInterface(Components.interfaces.nsIHttpChannel);
chan.setRequestHeader("If-None-Match", '"foo"', false);
chan.asyncOpen(new ChannelListener(consume304, null), null);
});
});

View File

@ -75,12 +75,6 @@ var tests = [
];
function getCacheService()
{
return Components.classes["@mozilla.org/network/cache-service;1"].
getService(Components.interfaces.nsICacheService);
}
function logit(i, data, ctx) {
dump("requested [" + tests[i].server + "] " +
"got [" + data + "] " +
@ -135,8 +129,8 @@ function run_test() {
httpserver.start(4444);
// clear cache
getCacheService().
evictEntries(Components.interfaces.nsICache.STORE_ANYWHERE);
evict_cache_entries();
triggerNextTest();
do_test_pending();
}

View File

@ -12,18 +12,15 @@ const kMemoryDevice = "memory";
const kOfflineDevice = "offline";
const kPrivate = "private";
const kCacheA = "cache-A";
const kCacheA2 = "cache-A2";
const kCacheB = "cache-B";
const kCacheC = "cache-C";
const kTestContent = "test content";
// the name for our cache session
const kPrivateBrowsing = "PrivateBrowsing";
var _CSvc;
function get_cache_service() {
if (_CSvc)
return _CSvc;
return _CSvc = Cc["@mozilla.org/network/cache-service;1"].
getService(Ci.nsICacheService);
}
function check_devices_available(devices) {
var cs = get_cache_service();
var found_devices = [];
@ -77,32 +74,6 @@ function get_device_entry_count(device) {
return entry_count;
}
function store_in_cache(aKey, aContent, aWhere) {
var storageFlag, streaming = true;
if (aWhere == kDiskDevice)
storageFlag = Ci.nsICache.STORE_ON_DISK;
else if (aWhere == kOfflineDevice)
storageFlag = Ci.nsICache.STORE_OFFLINE;
else if (aWhere == kMemoryDevice || aWhere == kPrivate)
storageFlag = Ci.nsICache.STORE_IN_MEMORY;
var cache = get_cache_service();
var session = cache.createSession(kPrivateBrowsing, storageFlag, streaming);
session.isPrivate = aWhere == kPrivate;
var cacheEntry = session.openCacheEntry(aKey, Ci.nsICache.ACCESS_WRITE, true);
var oStream = cacheEntry.openOutputStream(0);
var written = oStream.write(aContent, aContent.length);
if (written != aContent.length) {
do_throw("oStream.write has not written all data!\n" +
" Expected: " + aContent.length + "\n" +
" Actual: " + written + "\n");
}
oStream.close();
cacheEntry.close();
}
function make_input_stream_scriptable(input) {
var wrapper = Cc["@mozilla.org/scriptableinputstream;1"].
createInstance(Ci.nsIScriptableInputStream);
@ -110,85 +81,153 @@ function make_input_stream_scriptable(input) {
return wrapper;
}
function retrieve_from_cache(aKey, aWhere) {
var storageFlag, streaming = true;
if (aWhere == kDiskDevice)
storageFlag = Ci.nsICache.STORE_ANYWHERE;
else if (aWhere == kOfflineDevice)
storageFlag = Ci.nsICache.STORE_OFFLINE;
else if (aWhere == kMemoryDevice || aWhere == kPrivate)
storageFlag = Ci.nsICache.STORE_ANYWHERE;
var cache = get_cache_service();
var session = cache.createSession(kPrivateBrowsing, storageFlag, streaming);
session.isPrivate = aWhere == kPrivate;
try {
var cacheEntry = session.openCacheEntry(aKey, Ci.nsICache.ACCESS_READ, true);
} catch (e) {
if (e.result == Cr.NS_ERROR_CACHE_KEY_NOT_FOUND ||
e.result == Cr.NS_ERROR_FAILURE)
// a key not found error is expected here, so we will simply return null
// to let the caller know that no data was retrieved. We also expect
// a generic failure error in case of the offline cache.
return null;
const entries = [
// key content device should exist after leaving PB
[kCacheA, kTestContent, kMemoryDevice, true],
[kCacheA2, kTestContent, kPrivate, false],
[kCacheB, kTestContent, kDiskDevice, true],
[kCacheC, kTestContent, kOfflineDevice, true]
]
// Throw the textual error description.
do_throw(e);
function get_storage_policy(device)
{
switch (device) {
case kDiskDevice:
return Ci.nsICache.STORE_ON_DISK;
case kOfflineDevice:
return Ci.nsICache.STORE_OFFLINE;
case kMemoryDevice:
case kPrivate:
return Ci.nsICache.STORE_IN_MEMORY;
}
var iStream = make_input_stream_scriptable(cacheEntry.openInputStream(0));
var read = iStream.read(iStream.available());
iStream.close();
cacheEntry.close();
return read;
do_throw("unknown device");
}
function run_test() {
const kCacheA = "cache-A",
kCacheA2 = "cache-A2",
kCacheB = "cache-B",
kCacheC = "cache-C",
kTestContent = "test content";
var store_idx;
var store_cb = null;
function store_entries(cb)
{
if (cb) {
store_cb = cb;
store_idx = 0;
}
if (store_idx == entries.length) {
do_execute_soon(store_cb);
return;
}
var cache = get_cache_service();
var session = cache.createSession(kPrivateBrowsing,
get_storage_policy(entries[store_idx][2]),
Ci.nsICache.STREAM_BASED);
if (entries[store_idx][2] == kPrivate) {
session.isPrivate = true;
}
var cacheEntry = session.asyncOpenCacheEntry(entries[store_idx][0],
Ci.nsICache.ACCESS_WRITE,
store_data);
}
var store_data = {
onCacheEntryAvailable: function oCEA(entry, access, status) {
do_check_eq(status, Cr.NS_OK);
var os = entry.openOutputStream(0);
var written = os.write(entries[store_idx][1], entries[store_idx][1].length);
if (written != entries[store_idx][1].length) {
do_throw("os.write has not written all data!\n" +
" Expected: " + entries[store_idx][1].length + "\n" +
" Actual: " + written + "\n");
}
os.close();
entry.close();
store_idx++;
do_execute_soon(store_entries);
}
};
var check_idx;
var check_cb = null;
var check_pb_exited;
function check_entries(cb, pbExited)
{
if (cb) {
check_cb = cb;
check_idx = 0;
check_pb_exited = pbExited;
}
if (check_idx == entries.length) {
do_execute_soon(check_cb);
return;
}
var cache = get_cache_service();
var session = cache.createSession(kPrivateBrowsing,
get_storage_policy(entries[check_idx][2]),
Ci.nsICache.STREAM_BASED);
if (entries[check_idx][2] == kPrivate) {
session.isPrivate = true;
}
var cacheEntry = session.asyncOpenCacheEntry(entries[check_idx][0],
Ci.nsICache.ACCESS_READ,
check_data);
}
var check_data = {
onCacheEntryAvailable: function oCEA(entry, access, status) {
if (!check_pb_exited || entries[check_idx][3]) {
do_check_eq(status, Cr.NS_OK);
var is = make_input_stream_scriptable(entry.openInputStream(0));
var read = is.read(is.available());
is.close();
entry.close();
do_check_eq(read, entries[check_idx][1]);
} else {
do_check_eq(status, Cr.NS_ERROR_CACHE_KEY_NOT_FOUND);
}
check_idx++;
do_execute_soon(check_entries);
}
};
function run_test() {
// Simulate a profile dir for xpcshell
do_get_profile();
var cs = get_cache_service();
// Start off with an empty cache
cs.evictEntries(Ci.nsICache.STORE_ANYWHERE);
evict_cache_entries();
// Store cache-A, cache-A2, cache-B and cache-C
store_in_cache(kCacheA, kTestContent, kMemoryDevice);
store_in_cache(kCacheA2, kTestContent, kPrivate);
store_in_cache(kCacheB, kTestContent, kDiskDevice);
store_in_cache(kCacheC, kTestContent, kOfflineDevice);
store_entries(run_test2);
do_test_pending();
}
function run_test2() {
// Make sure all three cache devices are available initially
check_devices_available([kMemoryDevice, kDiskDevice, kOfflineDevice]);
// Check if cache-A, cache-A2, cache-B and cache-C are avilable
do_check_eq(retrieve_from_cache(kCacheA, kMemoryDevice), kTestContent);
do_check_eq(retrieve_from_cache(kCacheA2, kPrivate), kTestContent);
do_check_eq(retrieve_from_cache(kCacheB, kDiskDevice), kTestContent);
do_check_eq(retrieve_from_cache(kCacheC, kOfflineDevice), kTestContent);
// Check if cache-A, cache-A2, cache-B and cache-C are available
check_entries(run_test3, false);
}
function run_test3() {
// Simulate all private browsing instances being closed
var obsvc = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
obsvc.notifyObservers(null, "last-pb-context-exited", null);
// Make sure all three cache devices are still available
check_devices_available([kMemoryDevice, kDiskDevice, kOfflineDevice]);
// Make sure the memory device is not empty
do_check_eq(get_device_entry_count(kMemoryDevice), 1);
// Check if cache-A is gone, and cache-B and cache-C are still avilable
do_check_eq(retrieve_from_cache(kCacheA, kMemoryDevice), kTestContent);
do_check_eq(retrieve_from_cache(kCacheA2, kPrivate), null);
do_check_eq(retrieve_from_cache(kCacheB, kDiskDevice), kTestContent);
do_check_eq(retrieve_from_cache(kCacheC, kOfflineDevice), kTestContent);
// Check if cache-A is gone, and cache-B and cache-C are still available
check_entries(do_test_finished, true);
}

View File

@ -1,116 +1,103 @@
function getURLContent(aURL) {
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
const URL = "ftp://localhost/bug365133/";
var uri = ios.newURI(aURL, null, null);
var chan = ios.newChannelFromURI(uri);
var inp = chan.open();
var scinp = Components.classes["@mozilla.org/scriptableinputstream;1"].
createInstance(Components.interfaces.nsIScriptableInputStream);
scinp.init(inp);
var result = "";
var avail = scinp.available();
while (avail) {
result += scinp.read(avail);
avail = scinp.available();
}
return result;
const tests = [
[ /* Unix style listing, space at the end of filename */
"drwxrwxr-x 2 500 500 4096 Jan 01 2000 a \r\n"
,
"300: " + URL + "\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"a%20\" 0 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 DIRECTORY \n"
],
[ /* Unix style listing, space at the end of link name */
"lrwxrwxrwx 1 500 500 2 Jan 01 2000 l -> a \r\n"
,
"300: " + URL + "\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"l%20\" 2 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 SYMBOLIC-LINK \n"
],
[ /* Unix style listing, regular file with " -> " in name */
"-rw-rw-r-- 1 500 500 0 Jan 01 2000 arrow -> in name \r\n"
,
"300: " + URL + "\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"arrow%20-%3E%20in%20name%20\" 0 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n"
],
[ /* Unix style listing, tab at the end of filename */
"drwxrwxrwx 2 500 500 4096 Jan 01 2000 t \r\n"
,
"300: " + URL + "\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"t%09\" 0 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 DIRECTORY \n"
],
[ /* Unix style listing, multiple " -> " in filename */
"lrwxrwxrwx 1 500 500 26 Jan 01 2000 symlink with arrow -> in name -> file with arrow -> in name\r\n"
,
"300: " + URL + "\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"symlink%20with%20arrow%20-%3E%20in%20name\" 26 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 SYMBOLIC-LINK \n"
],
[ /* Unix style listing, multiple " -> " in filename, incorrect filesize */
"lrwxrwxrwx 1 500 500 0 Jan 01 2000 symlink with arrow -> in name -> file with arrow -> in name\r\n"
,
"300: " + URL + "\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"symlink%20with%20arrow%20-%3E%20in%20name%20-%3E%20file%20with%20arrow\" 0 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 SYMBOLIC-LINK \n"
],
[ /* DOS style listing, space at the end of filename, year 1999 */
"01-01-99 01:00AM 1024 file \r\n"
,
"300: " + URL + "\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"file%20\" 1024 Sun%2C%2001%20Jan%201999%2001%3A00%3A00 FILE \n"
],
[ /* DOS style listing, tab at the end of filename, year 2000 */
"01-01-00 01:00AM 1024 file \r\n"
,
"300: " + URL + "\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"file%09\" 1024 Sun%2C%2001%20Jan%202000%2001%3A00%3A00 FILE \n"
]
]
function checkData(request, data, ctx) {
do_check_eq(tests[0][1], data);
tests.shift();
next_test();
}
function storeCache(aURL, aContent) {
const nsICache = Components.interfaces.nsICache;
function storeData(status, entry) {
do_check_eq(status, Components.results.NS_OK);
entry.setMetaDataElement("servertype", "0");
var os = entry.openOutputStream(0);
var cache = Components.classes["@mozilla.org/network/cache-service;1"].
getService(Components.interfaces.nsICacheService);
var session = cache.createSession("FTP", nsICache.STORE_ANYWHERE, nsICache.STREAM_BASED);
var cacheEntry = session.openCacheEntry(aURL, nsICache.ACCESS_READ_WRITE, false);
cacheEntry.setMetaDataElement("servertype", "0");
var oStream = cacheEntry.openOutputStream(0);
var written = oStream.write(aContent, aContent.length);
if (written != aContent.length) {
do_throw("oStream.write have not written all data!\n" +
var written = os.write(tests[0][0], tests[0][0].length);
if (written != tests[0][0].length) {
do_throw("os.write has not written all data!\n" +
" Expected: " + written + "\n" +
" Actual: " + aContent.length + "\n");
" Actual: " + tests[0][0].length + "\n");
}
os.close();
entry.close();
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var channel = ios.newChannel(URL, "", null);
channel.asyncOpen(new ChannelListener(checkData, null, CL_ALLOW_UNKNOWN_CL), null);
}
function next_test() {
if (tests.length == 0)
do_test_finished();
else {
asyncOpenCacheEntry(URL,
"FTP",
Components.interfaces.nsICache.STORE_ANYWHERE,
Components.interfaces.nsICache.ACCESS_READ_WRITE,
storeData);
}
oStream.close();
cacheEntry.close();
}
function run_test() {
/* Unix style listing, space at the end of filename */
var rawData = "drwxrwxr-x 2 500 500 4096 Jan 01 2000 a \r\n";
storeCache("ftp://localhost/bug365133/", rawData);
var parsedData = getURLContent("ftp://localhost/bug365133/");
do_check_eq(parsedData,
"300: ftp://localhost/bug365133/\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"a%20\" 0 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 DIRECTORY \n");
/* Unix style listing, space at the end of link name */
rawData = "lrwxrwxrwx 1 500 500 2 Jan 01 2000 l -> a \r\n";
storeCache("ftp://localhost/bug365133/", rawData);
parsedData = getURLContent("ftp://localhost/bug365133/");
do_check_eq(parsedData,
"300: ftp://localhost/bug365133/\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"l%20\" 2 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 SYMBOLIC-LINK \n");
/* Unix style listing, regular file with " -> " in name */
rawData = "-rw-rw-r-- 1 500 500 0 Jan 01 2000 arrow -> in name \r\n";
storeCache("ftp://localhost/bug365133/", rawData);
parsedData = getURLContent("ftp://localhost/bug365133/");
do_check_eq(parsedData,
"300: ftp://localhost/bug365133/\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"arrow%20-%3E%20in%20name%20\" 0 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 FILE \n");
/* Unix style listing, tab at the end of filename */
rawData = "drwxrwxrwx 2 500 500 4096 Jan 01 2000 t \r\n";
storeCache("ftp://localhost/bug365133/", rawData);
parsedData = getURLContent("ftp://localhost/bug365133/");
do_check_eq(parsedData,
"300: ftp://localhost/bug365133/\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"t%09\" 0 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 DIRECTORY \n");
/* Unix style listing, multiple " -> " in filename */
rawData = "lrwxrwxrwx 1 500 500 26 Jan 01 2000 symlink with arrow -> in name -> file with arrow -> in name\r\n";
storeCache("ftp://localhost/bug365133/", rawData);
parsedData = getURLContent("ftp://localhost/bug365133/");
do_check_eq(parsedData,
"300: ftp://localhost/bug365133/\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"symlink%20with%20arrow%20-%3E%20in%20name\" 26 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 SYMBOLIC-LINK \n");
/* Unix style listing, multiple " -> " in filename, incorrect filesize */
rawData = "lrwxrwxrwx 1 500 500 0 Jan 01 2000 symlink with arrow -> in name -> file with arrow -> in name\r\n";
storeCache("ftp://localhost/bug365133/", rawData);
parsedData = getURLContent("ftp://localhost/bug365133/");
do_check_eq(parsedData,
"300: ftp://localhost/bug365133/\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"symlink%20with%20arrow%20-%3E%20in%20name%20-%3E%20file%20with%20arrow\" 0 Sun%2C%2001%20Jan%202000%2000%3A00%3A00 SYMBOLIC-LINK \n");
/* DOS style listing, space at the end of filename, year 1999 */
rawData = "01-01-99 01:00AM 1024 file \r\n";
storeCache("ftp://localhost/bug365133/", rawData);
parsedData = getURLContent("ftp://localhost/bug365133/");
do_check_eq(parsedData,
"300: ftp://localhost/bug365133/\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"file%20\" 1024 Sun%2C%2001%20Jan%201999%2001%3A00%3A00 FILE \n");
/* DOS style listing, tab at the end of filename, year 2000 */
rawData = "01-01-00 01:00AM 1024 file \r\n";
storeCache("ftp://localhost/bug365133/", rawData);
parsedData = getURLContent("ftp://localhost/bug365133/");
do_check_eq(parsedData,
"300: ftp://localhost/bug365133/\n" +
"200: filename content-length last-modified file-type\n" +
"201: \"file%09\" 1024 Sun%2C%2001%20Jan%202000%2001%3A00%3A00 FILE \n");
do_execute_soon(next_test);
do_test_pending();
}

View File

@ -32,11 +32,6 @@ var tests = [
];
function getCacheService() {
return Components.classes["@mozilla.org/network/cache-service;1"]
.getService(Components.interfaces.nsICacheService);
}
function setupChannel(suffix, value, cookie) {
var ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
@ -72,8 +67,7 @@ function run_test() {
httpserver.start(4444);
// Clear cache and trigger the first test
getCacheService().evictEntries(
Components.interfaces.nsICache.STORE_ANYWHERE);
evict_cache_entries();
triggerNextTest();
do_test_pending();

View File

@ -46,12 +46,6 @@ var tests = [
{url: "/freshness", server: "99", expected: "0"}, // cached
];
function getCacheService()
{
return Components.classes["@mozilla.org/network/cache-service;1"].
getService(Components.interfaces.nsICacheService);
}
function logit(i, data) {
dump(tests[i].url + "\t requested [" + tests[i].server + "]" +
" got [" + data + "] expected [" + tests[i].expected + "]");
@ -92,8 +86,7 @@ function run_test() {
httpserver.start(4444);
// clear cache
getCacheService().
evictEntries(Components.interfaces.nsICache.STORE_ANYWHERE);
evict_cache_entries();
triggerNextTest();
do_test_pending();

View File

@ -83,17 +83,12 @@ function makeChan(url) {
return chan;
}
function storeCache(aURL, aResponseHeads, aContent) {
var cache = Cc["@mozilla.org/network/cache-service;1"].
getService(Ci.nsICacheService);
function storeCache(aCacheEntry, aResponseHeads, aContent) {
aCacheEntry.setMetaDataElement("request-method", "GET");
aCacheEntry.setMetaDataElement("response-head", aResponseHeads);
aCacheEntry.setMetaDataElement("charset", "ISO-8859-1");
var session = cache.createSession("HTTP", Ci.nsICache.STORE_ANYWHERE, Ci.nsICache.STREAM_BASED);
var cacheEntry = session.openCacheEntry(aURL, Ci.nsICache.ACCESS_READ_WRITE, false);
cacheEntry.setMetaDataElement("request-method", "GET");
cacheEntry.setMetaDataElement("response-head", aResponseHeads);
cacheEntry.setMetaDataElement("charset", "ISO-8859-1");
var oStream = cacheEntry.openOutputStream(0);
var oStream = aCacheEntry.openOutputStream(0);
var written = oStream.write(aContent, aContent.length);
if (written != aContent.length) {
do_throw("oStream.write has not written all data!\n" +
@ -101,7 +96,7 @@ function storeCache(aURL, aResponseHeads, aContent) {
" Actual: " + aContent.length + "\n");
}
oStream.close();
cacheEntry.close();
aCacheEntry.close();
}
function test_nocache() {
@ -112,7 +107,16 @@ function test_nocache() {
}
function test_partial() {
storeCache("http://localhost:4444/bug482601/partial",
asyncOpenCacheEntry("http://localhost:4444/bug482601/partial",
"HTTP",
Ci.nsICache.STORE_ANYWHERE,
Ci.nsICache.ACCESS_READ_WRITE,
test_partial2);
}
function test_partial2(status, entry) {
do_check_eq(status, Cr.NS_OK);
storeCache(entry,
"HTTP/1.1 200 OK\r\n" +
"Date: Thu, 1 Jan 2009 00:00:00 GMT\r\n" +
"Server: httpd.js\r\n" +
@ -129,7 +133,16 @@ function test_partial() {
}
function test_cached() {
storeCache("http://localhost:4444/bug482601/cached",
asyncOpenCacheEntry("http://localhost:4444/bug482601/cached",
"HTTP",
Ci.nsICache.STORE_ANYWHERE,
Ci.nsICache.ACCESS_READ_WRITE,
test_cached2);
}
function test_cached2(status, entry) {
do_check_eq(status, Cr.NS_OK);
storeCache(entry,
"HTTP/1.1 200 OK\r\n" +
"Date: Thu, 1 Jan 2009 00:00:00 GMT\r\n" +
"Server: httpd.js\r\n" +
@ -147,7 +160,16 @@ function test_cached() {
}
function test_only_from_cache() {
storeCache("http://localhost:4444/bug482601/only_from_cache",
asyncOpenCacheEntry("http://localhost:4444/bug482601/only_from_cache",
"HTTP",
Ci.nsICache.STORE_ANYWHERE,
Ci.nsICache.ACCESS_READ_WRITE,
test_only_from_cache2);
}
function test_only_from_cache2(status, entry) {
do_check_eq(status, Cr.NS_OK);
storeCache(entry,
"HTTP/1.1 200 OK\r\n" +
"Date: Thu, 1 Jan 2009 00:00:00 GMT\r\n" +
"Server: httpd.js\r\n" +

View File

@ -1,45 +1,3 @@
function getURLContent(aURL) {
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var uri = ios.newURI(aURL, null, null);
var chan = ios.newChannelFromURI(uri);
var inp = chan.open();
var scinp = Components.classes["@mozilla.org/scriptableinputstream;1"].
createInstance(Components.interfaces.nsIScriptableInputStream);
scinp.init(inp);
var result = "";
var avail = scinp.available();
while (avail) {
result += scinp.read(avail);
avail = scinp.available();
}
return result;
}
function storeCache(aURL, aContent) {
const nsICache = Components.interfaces.nsICache;
var cache = Components.classes["@mozilla.org/network/cache-service;1"].
getService(Components.interfaces.nsICacheService);
var session = cache.createSession("FTP", nsICache.STORE_ANYWHERE, nsICache.STREAM_BASED);
var cacheEntry = session.openCacheEntry(aURL, nsICache.ACCESS_READ_WRITE, false);
cacheEntry.setMetaDataElement("servertype", "0");
var oStream = cacheEntry.openOutputStream(0);
var written = oStream.write(aContent, aContent.length);
if (written != aContent.length) {
do_throw("oStream.write has not written all data!\n" +
" Expected: " + written + "\n" +
" Actual: " + aContent.length + "\n");
}
oStream.close();
cacheEntry.close();
}
const URL = "ftp://localhost/bug464884/";
const tests = [
@ -105,10 +63,45 @@ const tests = [
"201: \"file2\" 95077 Sun%2C%2001%20Jan%202000%2001%3A00%3A00 FILE \n"]
]
function run_test() {
for (var i = 0; i < tests.length; i++) {
storeCache(URL, tests[i][0]);
var parsedData = getURLContent(URL);
do_check_eq(parsedData, tests[i][1]);
function checkData(request, data, ctx) {
do_check_eq(tests[0][1], data);
tests.shift();
next_test();
}
function storeData(status, entry) {
do_check_eq(status, Components.results.NS_OK);
entry.setMetaDataElement("servertype", "0");
var os = entry.openOutputStream(0);
var written = os.write(tests[0][0], tests[0][0].length);
if (written != tests[0][0].length) {
do_throw("os.write has not written all data!\n" +
" Expected: " + written + "\n" +
" Actual: " + tests[0][0].length + "\n");
}
os.close();
entry.close();
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var channel = ios.newChannel(URL, "", null);
channel.asyncOpen(new ChannelListener(checkData, null, CL_ALLOW_UNKNOWN_CL), null);
}
function next_test() {
if (tests.length == 0)
do_test_finished();
else {
asyncOpenCacheEntry(URL,
"FTP",
Components.interfaces.nsICache.STORE_ANYWHERE,
Components.interfaces.nsICache.ACCESS_READ_WRITE,
storeData);
}
}
function run_test() {
do_execute_soon(next_test);
do_test_pending();
}

View File

@ -33,12 +33,6 @@ var tests = [
];
function getCacheService()
{
return Components.classes["@mozilla.org/network/cache-service;1"].
getService(Components.interfaces.nsICacheService);
}
function logit(i, data) {
dump(tests[i].url + "\t requested [" + tests[i].server + "]" +
" got [" + data + "] expected [" + tests[i].expected + "]");
@ -83,8 +77,8 @@ function run_test() {
httpserver.start(4444);
// clear cache
getCacheService().
evictEntries(Components.interfaces.nsICache.STORE_ANYWHERE);
evict_cache_entries();
triggerNextTest();
do_test_pending();

View File

@ -7,11 +7,6 @@ var tests = [
{ url : "/bug510359", server : "1", expected : "1"},
];
function getCacheService() {
return Components.classes["@mozilla.org/network/cache-service;1"]
.getService(Components.interfaces.nsICacheService);
}
function setupChannel(suffix, value) {
var ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
@ -44,8 +39,8 @@ function run_test() {
httpserver.start(4444);
// clear cache
getCacheService().evictEntries(
Components.interfaces.nsICache.STORE_ANYWHERE);
evict_cache_entries();
triggerNextTest();
do_test_pending();

View File

@ -1,45 +1,3 @@
function getURLContent(aURL) {
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var uri = ios.newURI(aURL, null, null);
var chan = ios.newChannelFromURI(uri);
var inp = chan.open();
var scinp = Components.classes["@mozilla.org/scriptableinputstream;1"].
createInstance(Components.interfaces.nsIScriptableInputStream);
scinp.init(inp);
var result = "";
var avail = scinp.available();
while (avail) {
result += scinp.read(avail);
avail = scinp.available();
}
return result;
}
function storeCache(aURL, aContent) {
const nsICache = Components.interfaces.nsICache;
var cache = Components.classes["@mozilla.org/network/cache-service;1"].
getService(Components.interfaces.nsICacheService);
var session = cache.createSession("FTP", nsICache.STORE_ANYWHERE, nsICache.STREAM_BASED);
var cacheEntry = session.openCacheEntry(aURL, nsICache.ACCESS_READ_WRITE, false);
cacheEntry.setMetaDataElement("servertype", "0");
var oStream = cacheEntry.openOutputStream(0);
var written = oStream.write(aContent, aContent.length);
if (written != aContent.length) {
do_throw("oStream.write has not written all data!\n" +
" Expected: " + written + "\n" +
" Actual: " + aContent.length + "\n");
}
oStream.close();
cacheEntry.close();
}
const URL = "ftp://localhost/bug515583/";
const tests = [
@ -63,10 +21,45 @@ const tests = [
"200: filename content-length last-modified file-type\n"]
]
function run_test() {
for (var i = 0; i < tests.length; i++) {
storeCache(URL, tests[i][0]);
var parsedData = getURLContent(URL);
do_check_eq(parsedData, tests[i][1]);
function checkData(request, data, ctx) {
do_check_eq(tests[0][1], data);
tests.shift();
next_test();
}
function storeData(status, entry) {
do_check_eq(status, Components.results.NS_OK);
entry.setMetaDataElement("servertype", "0");
var os = entry.openOutputStream(0);
var written = os.write(tests[0][0], tests[0][0].length);
if (written != tests[0][0].length) {
do_throw("os.write has not written all data!\n" +
" Expected: " + written + "\n" +
" Actual: " + tests[0][0].length + "\n");
}
os.close();
entry.close();
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var channel = ios.newChannel(URL, "", null);
channel.asyncOpen(new ChannelListener(checkData, null, CL_ALLOW_UNKNOWN_CL), null);
}
function next_test() {
if (tests.length == 0)
do_test_finished();
else {
asyncOpenCacheEntry(URL,
"FTP",
Components.interfaces.nsICache.STORE_ANYWHERE,
Components.interfaces.nsICache.ACCESS_READ_WRITE,
storeData);
}
}
function run_test() {
do_execute_soon(next_test);
do_test_pending();
}

View File

@ -2,14 +2,18 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
function run_test() {
var cs = Components.classes["@mozilla.org/network/cache-service;1"].
getService(Components.interfaces.nsICacheService);
var nsICache = Components.interfaces.nsICache;
var session = cs.createSession("client",
nsICache.STORE_ANYWHERE,
true);
var entry = session.openCacheEntry("key", nsICache.STORE_ON_DISK, true);
function continue_test(status, entry) {
do_check_eq(status, Components.results.NS_OK);
entry.deviceID;
// if the above line does not crash, the test was successful
do_test_finished();
}
function run_test() {
asyncOpenCacheEntry("key",
"client",
Components.interfaces.nsICache.STORE_ANYWHERE,
Components.interfaces.nsICache.ACCESS_WRITE,
continue_test);
do_test_pending();
}

View File

@ -1,45 +1,3 @@
function getURLContent(aURL) {
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var uri = ios.newURI(aURL, null, null);
var chan = ios.newChannelFromURI(uri);
var inp = chan.open();
var scinp = Components.classes["@mozilla.org/scriptableinputstream;1"].
createInstance(Components.interfaces.nsIScriptableInputStream);
scinp.init(inp);
var result = "";
var avail = scinp.available();
while (avail) {
result += scinp.read(avail);
avail = scinp.available();
}
return result;
}
function storeCache(aURL, aContent) {
const nsICache = Components.interfaces.nsICache;
var cache = Components.classes["@mozilla.org/network/cache-service;1"].
getService(Components.interfaces.nsICacheService);
var session = cache.createSession("FTP", nsICache.STORE_ANYWHERE, nsICache.STREAM_BASED);
var cacheEntry = session.openCacheEntry(aURL, nsICache.ACCESS_READ_WRITE, false);
cacheEntry.setMetaDataElement("servertype", "0");
var oStream = cacheEntry.openOutputStream(0);
var written = oStream.write(aContent, aContent.length);
if (written != aContent.length) {
do_throw("oStream.write has not written all data!\n" +
" Expected: " + written + "\n" +
" Actual: " + aContent.length + "\n");
}
oStream.close();
cacheEntry.close();
}
const URL = "ftp://localhost/bug543805/";
var year = new Date().getFullYear().toString();
@ -81,10 +39,45 @@ const tests = [
"201: \"test2.file\" 66 Sun%2C%2001%20Apr%202008%2000%3A00%3A00 FILE \n"]
]
function run_test() {
for (var i = 0; i < tests.length; i++) {
storeCache(URL, tests[i][0]);
var parsedData = getURLContent(URL);
do_check_eq(parsedData, tests[i][1]);
function checkData(request, data, ctx) {
do_check_eq(tests[0][1], data);
tests.shift();
next_test();
}
function storeData(status, entry) {
do_check_eq(status, Components.results.NS_OK);
entry.setMetaDataElement("servertype", "0");
var os = entry.openOutputStream(0);
var written = os.write(tests[0][0], tests[0][0].length);
if (written != tests[0][0].length) {
do_throw("os.write has not written all data!\n" +
" Expected: " + written + "\n" +
" Actual: " + tests[0][0].length + "\n");
}
os.close();
entry.close();
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var channel = ios.newChannel(URL, "", null);
channel.asyncOpen(new ChannelListener(checkData, null, CL_ALLOW_UNKNOWN_CL), null);
}
function next_test() {
if (tests.length == 0)
do_test_finished();
else {
asyncOpenCacheEntry(URL,
"FTP",
Components.interfaces.nsICache.STORE_ANYWHERE,
Components.interfaces.nsICache.ACCESS_READ_WRITE,
storeData);
}
}
function run_test() {
do_execute_soon(next_test);
do_test_pending();
}

View File

@ -8,12 +8,6 @@ do_load_httpd_js();
var httpserver = new nsHttpServer();
var iteration = 0;
function getCacheService()
{
return Components.classes["@mozilla.org/network/cache-service;1"]
.getService(Components.interfaces.nsICacheService);
}
function setupChannel(suffix)
{
var ios =
@ -38,8 +32,7 @@ function run_test()
httpserver.start(4444);
// clear cache
getCacheService().
evictEntries(Components.interfaces.nsICache.STORE_ANYWHERE);
evict_cache_entries();
// load first time
var channel = setupChannel("/redirect1");

View File

@ -35,12 +35,6 @@ function checkValue(request, data, ctx) {
httpserv.stop(do_test_finished);
}
function getCacheService()
{
return Components.classes["@mozilla.org/network/cache-service;1"]
.getService(Components.interfaces.nsICacheService);
}
function makeChan(url) {
var ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
@ -70,8 +64,7 @@ function run_test() {
Components.interfaces.nsIProtocolProxyService.PROXYCONFIG_SYSTEM);
// clear cache
getCacheService().
evictEntries(Components.interfaces.nsICache.STORE_ANYWHERE);
evict_cache_entries();
var chan = makeChan("http://localhost:4444/target");
chan.asyncOpen(new ChannelListener(checkValue, null), null);

View File

@ -2,11 +2,6 @@ do_load_httpd_js();
var httpserver = new nsHttpServer();
var expectedOnStopRequests = 3;
function getCacheService() {
return Components.classes["@mozilla.org/network/cache-service;1"]
.getService(Components.interfaces.nsICacheService);
}
function setupChannel(suffix, xRequest, flags) {
var ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
@ -58,8 +53,7 @@ function run_test() {
do_get_profile();
// clear cache
getCacheService().evictEntries(
Components.interfaces.nsICache.STORE_ANYWHERE);
evict_cache_entries();
var ch0 = setupChannel("/bug596443", "Response0", Ci.nsIRequest.LOAD_BYPASS_CACHE);
ch0.asyncOpen(new Listener("Response0"), null);

View File

@ -14,11 +14,6 @@ do_load_httpd_js();
var httpserv;
function getCacheService() {
return Components.classes["@mozilla.org/network/cache-service;1"]
.getService(Components.interfaces.nsICacheService);
}
function setupChannel(path) {
var ios =
Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
@ -85,8 +80,7 @@ function run_test() {
httpserv.start(4444);
// Clear cache
getCacheService().evictEntries(
Components.interfaces.nsICache.STORE_ANYWHERE);
evict_cache_entries();
// Load Content-Location URI into cache and start the chain of loads
var channel = setupChannel("http://localhost:4444/cl");

View File

@ -6,15 +6,6 @@ const CACHECTRL_HDR_NAME = "X-CACHE-CONTROL-HEADER";
var httpserver = null;
var _CSvc;
function get_cache_service() {
if (_CSvc)
return _CSvc;
return _CSvc = Cc["@mozilla.org/network/cache-service;1"].
getService(Ci.nsICacheService);
}
function make_channel(flags, vary, value) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
@ -182,7 +173,7 @@ function handler(metadata, response) {
function run_test() {
// clear the cache
get_cache_service().evictEntries(Ci.nsICache.STORE_ANYWHERE);
evict_cache_entries();
httpserver = new nsHttpServer();
httpserver.registerPathHandler("/bug633743", handler);

View File

@ -21,8 +21,7 @@ function SyncWithCacheThread(aFunc) {
do_check_eq(sync_with_cache_IO_thread_cb.listener, null);
sync_with_cache_IO_thread_cb.listener = aFunc;
var cache = Cc["@mozilla.org/network/cache-service;1"].
getService(Ci.nsICacheService);
var cache = get_cache_service();
var session = cache.createSession(
"HTTP",
Ci.nsICache.STORE_ANYWHERE,
@ -44,12 +43,6 @@ var sync_with_cache_IO_thread_cb = {
}
};
function clearCache() {
var service = Components.classes["@mozilla.org/network/cache-service;1"]
.getService(Ci.nsICacheService);
service.evictEntries(Ci.nsICache.STORE_ANYWHERE);
}
function setupChannel(suffix, value) {
var ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
@ -88,7 +81,7 @@ function nextTest() {
// We really want each test to be self-contained. Make sure cache is
// cleared and also let all operations finish before starting a new test
SyncWithCacheThread(function() {
clearCache();
evict_cache_entries();
SyncWithCacheThread(runNextTest);
});
}

View File

@ -2,15 +2,6 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
var _CSvc;
function get_cache_service() {
if (_CSvc)
return _CSvc;
return _CSvc = Cc["@mozilla.org/network/cache-service;1"].
getService(Ci.nsICacheService);
}
var _PSvc;
function get_pref_service() {
if (_PSvc)
@ -20,49 +11,6 @@ function get_pref_service() {
getService(Ci.nsIPrefBranch);
}
function get_ostream_for_entry(key, asFile, append, entryRef)
{
var cache = get_cache_service();
var session = cache.createSession(
"HTTP",
asFile ? Ci.nsICache.STORE_ON_DISK_AS_FILE
: Ci.nsICache.STORE_ON_DISK,
Ci.nsICache.STREAM_BASED);
var cacheEntry = session.openCacheEntry(
key,
append ? Ci.nsICache.ACCESS_READ_WRITE
: Ci.nsICache.ACCESS_WRITE,
true);
var oStream = cacheEntry.openOutputStream(append ? cacheEntry.dataSize : 0);
entryRef.value = cacheEntry;
return oStream;
}
function sync_with_cache_IO_thread(aFunc)
{
do_check_eq(sync_with_cache_IO_thread_cb.listener, null);
sync_with_cache_IO_thread_cb.listener = aFunc;
var cache = get_cache_service();
var session = cache.createSession(
"HTTP",
Ci.nsICache.STORE_ON_DISK,
Ci.nsICache.STREAM_BASED);
var cacheEntry = session.asyncOpenCacheEntry(
"nonexistententry",
Ci.nsICache.ACCESS_READ,
sync_with_cache_IO_thread_cb);
}
var sync_with_cache_IO_thread_cb = {
listener: null,
onCacheEntryAvailable: function oCEA(descriptor, accessGranted, status) {
do_check_eq(status, Cr.NS_ERROR_CACHE_KEY_NOT_FOUND);
cb = this.listener;
this.listener = null;
do_timeout(0, cb);
}
};
function gen_1MiB()
{
var i;
@ -82,10 +30,10 @@ function write_and_check(str, data, len)
}
}
function write_datafile()
function write_datafile(status, entry)
{
var entry = {};
var oStr = get_ostream_for_entry("data", true, false, entry);
do_check_eq(status, Cr.NS_OK);
var os = entry.openOutputStream(0);
var data = gen_1MiB();
// max size in MB
@ -95,50 +43,56 @@ function write_datafile()
// write larger entry than is allowed
var i;
for (i=0 ; i<(max_size+1) ; i++)
write_and_check(oStr, data, data.length);
write_and_check(os, data, data.length);
oStr.close();
entry.value.close();
os.close();
entry.close();
// wait until writing is done, then append to entry
sync_with_cache_IO_thread(append_datafile);
// append to entry
asyncOpenCacheEntry("data",
"HTTP",
Ci.nsICache.STORE_ON_DISK,
Ci.nsICache.ACCESS_READ_WRITE,
append_datafile);
}
function append_datafile()
function append_datafile(status, entry)
{
var entry = {};
var oStr = get_ostream_for_entry("data", false, true, entry);
do_check_eq(status, Cr.NS_OK);
var os = entry.openOutputStream(entry.dataSize);
var data = gen_1MiB();
// append 1MiB
try {
write_and_check(oStr, data, data.length);
write_and_check(os, data, data.length);
do_throw();
}
catch (ex) { }
// closing the ostream should fail in this case
try {
oStr.close();
os.close();
do_throw();
}
catch (ex) { }
entry.value.close();
// wait until cache-operations have finished, then finish test
sync_with_cache_IO_thread(do_test_finished);
entry.close();
do_test_finished();
}
function run_test() {
do_get_profile();
// clear the cache
get_cache_service().evictEntries(Ci.nsICache.STORE_ANYWHERE);
evict_cache_entries();
// wait until clearing is done, then force to write file bigger than 5MiB
sync_with_cache_IO_thread(write_datafile);
// force to write file bigger than 5MiB
asyncOpenCacheEntry("data",
"HTTP",
Ci.nsICache.STORE_ON_DISK_AS_FILE,
Ci.nsICache.ACCESS_WRITE,
write_datafile);
do_test_pending();
}

View File

@ -2,33 +2,6 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
var _CSvc;
function get_cache_service() {
if (_CSvc)
return _CSvc;
return _CSvc = Cc["@mozilla.org/network/cache-service;1"].
getService(Ci.nsICacheService);
}
function get_ostream_for_entry(key, asFile, append, entryRef)
{
var cache = get_cache_service();
var session = cache.createSession(
"HTTP",
asFile ? Ci.nsICache.STORE_ON_DISK_AS_FILE
: Ci.nsICache.STORE_ON_DISK,
Ci.nsICache.STREAM_BASED);
var cacheEntry = session.openCacheEntry(
key,
append ? Ci.nsICache.ACCESS_READ_WRITE
: Ci.nsICache.ACCESS_WRITE,
true);
var oStream = cacheEntry.openOutputStream(append ? cacheEntry.dataSize : 0);
entryRef.value = cacheEntry;
return oStream;
}
function gen_1MiB()
{
var i;
@ -55,46 +28,58 @@ function make_input_stream_scriptable(input) {
return wrapper;
}
function write_datafile()
function write_datafile(status, entry)
{
var entry = {};
var oStr = get_ostream_for_entry("data", true, false, entry);
do_check_eq(status, Cr.NS_OK);
var os = entry.openOutputStream(0);
var data = gen_1MiB();
write_and_check(oStr, data, data.length);
write_and_check(os, data, data.length);
os.close();
entry.close();
// open, doom, append, read
asyncOpenCacheEntry("data",
"HTTP",
Ci.nsICache.STORE_ON_DISK,
Ci.nsICache.ACCESS_READ_WRITE,
test_read_after_doom);
oStr.close();
entry.value.close();
}
function test_read_after_doom()
function test_read_after_doom(status, entry)
{
var entry = {};
var oStr = get_ostream_for_entry("data", false, true, entry);
do_check_eq(status, Cr.NS_OK);
var os = entry.openOutputStream(entry.dataSize);
var data = gen_1MiB();
entry.value.doom();
write_and_check(oStr, data, data.length);
entry.doom();
write_and_check(os, data, data.length);
oStr.close();
os.close();
var iStr = make_input_stream_scriptable(entry.value.openInputStream(0));
var read = iStr.read(iStr.available());
var is = make_input_stream_scriptable(entry.openInputStream(0));
var read = is.read(is.available());
do_check_eq(read.length, 2*1024*1024);
iStr.close();
is.close();
entry.value.close();
entry.close();
do_test_finished();
}
function run_test() {
do_get_profile();
// clear the cache
get_cache_service().evictEntries(Ci.nsICache.STORE_ANYWHERE);
evict_cache_entries();
// force to write file bigger than 5MiB
write_datafile();
asyncOpenCacheEntry("data",
"HTTP",
Ci.nsICache.STORE_ON_DISK_AS_FILE,
Ci.nsICache.ACCESS_WRITE,
write_datafile);
// open, doom, append, read
test_read_after_doom();
do_test_pending();
}

View File

@ -2,33 +2,6 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
var _CSvc;
function get_cache_service() {
if (_CSvc)
return _CSvc;
return _CSvc = Cc["@mozilla.org/network/cache-service;1"].
getService(Ci.nsICacheService);
}
function get_ostream_for_entry(key, asFile, append, entryRef)
{
var cache = get_cache_service();
var session = cache.createSession(
"HTTP",
asFile ? Ci.nsICache.STORE_ON_DISK_AS_FILE
: Ci.nsICache.STORE_ON_DISK,
Ci.nsICache.STREAM_BASED);
var cacheEntry = session.openCacheEntry(
key,
append ? Ci.nsICache.ACCESS_READ_WRITE
: Ci.nsICache.ACCESS_WRITE,
true);
var oStream = cacheEntry.openOutputStream(append ? cacheEntry.dataSize : 0);
entryRef.value = cacheEntry;
return oStream;
}
function gen_1MiB()
{
var i;
@ -48,40 +21,51 @@ function write_and_check(str, data, len)
}
}
function write_datafile()
function write_datafile(status, entry)
{
var entry = {};
var oStr = get_ostream_for_entry("data", true, false, entry);
do_check_eq(status, Cr.NS_OK);
var os = entry.openOutputStream(0);
var data = gen_1MiB();
write_and_check(oStr, data, data.length);
write_and_check(os, data, data.length);
oStr.close();
entry.value.close();
os.close();
entry.close();
// try to open the entry for appending
asyncOpenCacheEntry("data",
"HTTP",
Ci.nsICache.STORE_ON_DISK,
Ci.nsICache.ACCESS_READ_WRITE,
open_for_readwrite);
}
function open_for_readwrite()
function open_for_readwrite(status, entry)
{
var entry = {};
var oStr = get_ostream_for_entry("data", false, true, entry);
do_check_eq(status, Cr.NS_OK);
var os = entry.openOutputStream(entry.dataSize);
// Opening the entry for appending data calls nsDiskCacheStreamIO::Seek()
// which initializes mFD. If no data is written then mBufDirty is false and
// mFD won't be closed in nsDiskCacheStreamIO::Flush().
oStr.close();
entry.value.close();
os.close();
entry.close();
do_test_finished();
}
function run_test() {
do_get_profile();
// clear the cache
get_cache_service().evictEntries(Ci.nsICache.STORE_ANYWHERE);
evict_cache_entries();
// force to write file bigger than 5MiB
write_datafile();
asyncOpenCacheEntry("data",
"HTTP",
Ci.nsICache.STORE_ON_DISK_AS_FILE,
Ci.nsICache.ACCESS_WRITE,
write_datafile);
// try to open the entry for appending
open_for_readwrite();
do_test_pending();
}

View File

@ -1,12 +1,6 @@
do_load_httpd_js();
var httpserver = new nsHttpServer();
function getCacheService()
{
return Components.classes["@mozilla.org/network/cache-service;1"]
.getService(Components.interfaces.nsICacheService);
}
function setupChannel(suffix)
{
var ios =
@ -29,8 +23,7 @@ function run_test()
httpserver.start(4444);
// clear cache
getCacheService().
evictEntries(Components.interfaces.nsICache.STORE_ANYWHERE);
evict_cache_entries();
// load first time
var channel = setupChannel("/redirect1");

View File

@ -2,7 +2,6 @@ var Ci = Components.interfaces;
var Cc = Components.classes;
var Cr = Components.results;
var _cacheSvc;
var _ios;
var ACCESS_WRITE = Ci.nsICache.ACCESS_WRITE;
@ -71,15 +70,6 @@ CacheVisitor.prototype = {
}
};
function get_cache_service() {
if (!_cacheSvc) {
_cacheSvc = Cc["@mozilla.org/network/cache-service;1"].
getService(Ci.nsICacheService);
}
return _cacheSvc
}
function get_io_service() {
if (!_ios) {
_ios = Cc["@mozilla.org/network/io-service;1"].
@ -141,8 +131,7 @@ function run_test() {
do_get_profile();
// Make sure the cache is empty
var cache = get_cache_service();
cache.evictEntries(Ci.nsICache.STORE_ANYWHERE);
evict_cache_entries();
// Add new tests at the end of this section
add_test(test_corrupt_secinfo);

View File

@ -0,0 +1,121 @@
"use strict";
// https://bugzilla.mozilla.org/show_bug.cgi?id=760955
do_load_httpd_js();
var httpServer = null;
const testFileName = "test_nsHttpChannel_CacheForOfflineUse-no-store";
const cacheClientID = testFileName;
const basePath = "/" + testFileName + "/";
const baseURI = "http://localhost:4444" + basePath;
const normalEntry = "normal";
const noStoreEntry = "no-store";
var cacheUpdateObserver = null;
function make_channel_for_offline_use(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var chan = ios.newChannel(url, "", null);
var cachingChan = chan.QueryInterface(Ci.nsICachingChannel);
cachingChan.cacheForOfflineUse = true;
cachingChan.offlineCacheClientID = cacheClientID;
return chan;
}
function make_uri(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newURI(url, null, null);
}
function CacheListener() { }
CacheListener.prototype = {
QueryInterface : function(iid)
{
if (iid.equals(Components.interfaces.nsICacheListener))
return this;
throw Components.results.NS_NOINTERFACE;
},
};
function asyncCheckCacheEntryExistance(entryName, shouldExist)
{
var listener = new CacheListener();
listener.onCacheEntryAvailable = function(descriptor, accessGranted, status) {
if (shouldExist) {
do_check_eq(status, Cr.NS_OK);
do_check_true(!!descriptor);
} else {
todo_check_eq(status, Cr.NS_ERROR_CACHE_KEY_NOT_FOUND); // bug 761040
todo_check_null(descriptor); // bug 761040
}
run_next_test();
};
var service = Cc["@mozilla.org/network/cache-service;1"]
.getService(Ci.nsICacheService);
var session = service.createSession(cacheClientID, Ci.nsICache.STORE_OFFLINE,
true);
session.asyncOpenCacheEntry(baseURI + entryName, Ci.nsICache.ACCESS_READ,
listener);
}
const responseBody = "response body";
// A HTTP channel for updating the offline cache should normally succeed.
function normalHandler(metadata, response)
{
do_print("normalHandler");
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
function checkNormal(request, buffer)
{
do_check_eq(buffer, responseBody);
asyncCheckCacheEntryExistance(normalEntry, true);
}
add_test(function test_normal() {
var chan = make_channel_for_offline_use(baseURI + normalEntry);
chan.asyncOpen(new ChannelListener(checkNormal, chan), null);
});
// An HTTP channel for updating the offline cache should fail when it gets a
// response with Cache-Control: no-store.
function noStoreHandler(metadata, response)
{
do_print("noStoreHandler");
response.setHeader("Content-Type", "text/plain");
response.setHeader("Cache-Control", "no-store");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
function checkNoStore(request, buffer)
{
todo_check_eq(buffer, ""); // bug 761040
asyncCheckCacheEntryExistance(noStoreEntry, false);
run_next_test();
}
add_test(function test_noStore() {
var chan = make_channel_for_offline_use(baseURI + noStoreEntry);
// The no-store should cause the channel to fail to load.
chan.asyncOpen(new ChannelListener(checkNoStore, chan
/*, TODO(bug 761040): CL_EXPECT_FAILURE*/),
null);
});
function run_test()
{
do_get_profile();
httpServer = new nsHttpServer();
httpServer.registerPathHandler(basePath + normalEntry, normalHandler);
httpServer.registerPathHandler(basePath + noStoreEntry, noStoreHandler);
httpServer.start(4444);
run_next_test();
}
function finish_test(request, buffer)
{
httpServer.stop(do_test_finished);
}

View File

@ -6,37 +6,6 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
var _CSvc;
function get_cache_service() {
if (_CSvc)
return _CSvc;
return _CSvc = Cc["@mozilla.org/network/cache-service;1"].
getService(Ci.nsICacheService);
}
function get_ostream_for_entry(key, append, compress, entryRef)
{
var cache = get_cache_service();
var session = cache.createSession(
"HTTP",
Ci.nsICache.STORE_ON_DISK,
Ci.nsICache.STREAM_BASED);
var cacheEntry = session.openCacheEntry(
key,
append ? Ci.nsICache.ACCESS_READ_WRITE
: Ci.nsICache.ACCESS_WRITE,
true);
if (compress)
if (!append)
cacheEntry.setMetaDataElement("uncompressed-len", "0");
var oStream = cacheEntry.openOutputStream(append ? cacheEntry.storageDataSize : 0);
entryRef.value = cacheEntry;
return oStream;
}
function write_and_check(str, data, len)
{
var written = str.write(data, len);
@ -47,60 +16,77 @@ function write_and_check(str, data, len)
}
}
function check_datafile()
function TestAppend(compress, callback)
{
var cache = get_cache_service();
var session = cache.createSession(
"HTTP",
Ci.nsICache.STORE_ON_DISK,
Ci.nsICache.STREAM_BASED);
var entry = session.openCacheEntry(
"data",
Ci.nsICache.ACCESS_READ,
true);
var wrapper = Cc["@mozilla.org/scriptableinputstream;1"].
createInstance(Ci.nsIScriptableInputStream);
wrapper.init(entry.openInputStream(0));
var str = wrapper.read(wrapper.available());
do_check_eq(str.length, 10);
do_check_eq(str, "12345abcde");
wrapper.close();
entry.close();
this._compress = compress;
this._callback = callback;
this.run();
}
TestAppend.prototype = {
_compress: false,
_callback: null,
function write_datafile(compress)
{
var entry = {};
var oStr = get_ostream_for_entry("data", false, compress, entry);
write_and_check(oStr, "12345", 5);
oStr.close();
entry.value.close();
}
run: function() {
evict_cache_entries();
asyncOpenCacheEntry("data",
"HTTP",
Ci.nsICache.STORE_ON_DISK,
Ci.nsICache.ACCESS_WRITE,
this.writeData.bind(this));
},
function append_datafile()
{
var entry = {};
var oStr = get_ostream_for_entry("data", true, false, entry);
write_and_check(oStr, "abcde", 5);
oStr.close();
entry.value.close();
}
writeData: function(status, entry) {
do_check_eq(status, Cr.NS_OK);
if (this._compress)
entry.setMetaDataElement("uncompressed-len", "0");
var os = entry.openOutputStream(0);
write_and_check(os, "12345", 5);
os.close();
entry.close();
asyncOpenCacheEntry("data",
"HTTP",
Ci.nsICache.STORE_ON_DISK,
Ci.nsICache.ACCESS_READ_WRITE,
this.appendData.bind(this));
},
function test_append(compress)
{
get_cache_service().evictEntries(Ci.nsICache.STORE_ANYWHERE);
write_datafile(compress);
append_datafile();
check_datafile();
}
appendData: function(status, entry) {
do_check_eq(status, Cr.NS_OK);
var os = entry.openOutputStream(entry.storageDataSize);
write_and_check(os, "abcde", 5);
os.close();
entry.close();
asyncOpenCacheEntry("data",
"HTTP",
Ci.nsICache.STORE_ON_DISK,
Ci.nsICache.ACCESS_READ,
this.checkData.bind(this));
},
checkData: function(status, entry) {
do_check_eq(status, Cr.NS_OK);
var wrapper = Cc["@mozilla.org/scriptableinputstream;1"].
createInstance(Ci.nsIScriptableInputStream);
wrapper.init(entry.openInputStream(0));
var str = wrapper.read(wrapper.available());
do_check_eq(str.length, 10);
do_check_eq(str, "12345abcde");
wrapper.close();
entry.close();
do_execute_soon(this._callback);
}
};
function run_test() {
do_get_profile();
test_append(false);
test_append(true);
new TestAppend(false, run_test2);
do_test_pending();
}
function run_test2() {
new TestAppend(true, do_test_finished);
}

View File

@ -10,15 +10,6 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
var _CSvc;
function get_cache_service() {
if (_CSvc)
return _CSvc;
return _CSvc = Cc["@mozilla.org/network/cache-service;1"].
getService(Ci.nsICacheService);
}
function GetOutputStreamForEntry(key, asFile, append, callback)
{
this._key = key;
@ -164,7 +155,7 @@ function run_test() {
do_get_profile();
// clear the cache
get_cache_service().evictEntries(Ci.nsICache.STORE_ANYWHERE);
evict_cache_entries();
write_entry();
do_test_pending();
}

View File

@ -8,13 +8,6 @@ const responseBody = [0x1f, 0x8b, 0x08, 0x08, 0xef, 0x70, 0xe6, 0x4c, 0x00, 0x03
0xe2, 0x9c, 0xcc, 0xf4, 0x8c, 0x92, 0x9c, 0x4a, 0x85, 0x9c, 0xfc, 0xbc, 0xf4, 0xd4, 0x22, 0x85,
0x92, 0xd4, 0xe2, 0x12, 0x2e, 0x2e, 0x00, 0x00, 0xe5, 0xe6, 0xf0, 0x20, 0x00, 0x00, 0x00];
function getCacheService()
{
var nsCacheService = Components.classes["@mozilla.org/network/cache-service;1"];
var service = nsCacheService.getService(Components.interfaces.nsICacheService);
return service;
}
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
@ -82,7 +75,7 @@ function run_test() {
httpserver.start(4444);
// wipe out cached content
getCacheService().evictEntries(Components.interfaces.nsICache.STORE_ANYWHERE);
evict_cache_entries();
var chan = make_channel("http://localhost:4444/cached/test.gz");
chan.asyncOpen(new ChannelListener(continue_test, null, CL_EXPECT_GZIP), null);

View File

@ -1,6 +1,5 @@
do_load_httpd_js();
var httpserver = new nsHttpServer();
var cacheService;
var ios;
// Test the handling of a cache revalidation with mismatching last-modified
@ -103,12 +102,10 @@ var listener_1 = {
function run_test() {
do_get_profile();
cacheService = Cc["@mozilla.org/network/cache-service;1"].
getService(Ci.nsICacheService);
ios = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
cacheService.evictEntries(Ci.nsICache.STORE_ANYWHERE);
evict_cache_entries();
httpserver.registerPathHandler("/test1", handler);
httpserver.start(4444);

View File

@ -38,8 +38,6 @@ Cu.import("resource://gre/modules/Services.jsm");
const kNS_OFFLINECACHEUPDATESERVICE_CONTRACTID =
"@mozilla.org/offlinecacheupdate-service;1";
const kNS_CACHESERVICE_CONTRACTID =
"@mozilla.org/network/cache-service;1";
const kManifest1 = "CACHE MANIFEST\n" +
"/pages/foo1\n" +
@ -120,9 +118,7 @@ function init_cache_capacity() {
}
function clean_app_cache() {
let cache_service = Cc[kNS_CACHESERVICE_CONTRACTID].
getService(Ci.nsICacheService);
cache_service.evictEntries(Ci.nsICache.STORE_OFFLINE);
evict_cache_entries(Ci.nsICache.STORE_OFFLINE);
}
function do_app_cache(manifestURL, pageURL, pinned) {

View File

@ -29,13 +29,6 @@ const decodedBody = [0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20,
const partial_data_length = 4;
function getCacheService()
{
var nsCacheService = Components.classes["@mozilla.org/network/cache-service;1"];
var service = nsCacheService.getService(Components.interfaces.nsICacheService);
return service;
}
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
@ -249,7 +242,7 @@ function run_test() {
httpserver.start(4444);
// wipe out cached content
getCacheService().evictEntries(Components.interfaces.nsICache.STORE_ANYWHERE);
evict_cache_entries();
// Case 2: zero-length partial entry must not trigger range-request
var chan = make_channel("http://localhost:4444/test_2");

View File

@ -1,7 +1,9 @@
[DEFAULT]
head = head_channels.js
head = head_channels.js head_cache.js
tail =
[test_304_responses.js]
[test_cacheForOfflineUse_no-store.js]
[test_307_redirect.js]
[test_NetUtil.js]
[test_URIs.js]

View File

@ -17,6 +17,7 @@ Alex Grönholm
Anatoly Techtonik
Antonio Cuni
Armin Ronacher
Bradley Ayers
Cap Petschulat
CBWhiz
Chris McDonough
@ -24,6 +25,7 @@ Christian Stefanescu
Christopher Nilsson
Cliff Xuan
Curt Micol
David Schoonover
Doug Hellmann
Doug Napoleone
Douglas Creager
@ -35,9 +37,14 @@ Jeff Hammel
Jonathan Griffin
Jorge Vargas
Josh Bronson
Konstantin Zemlyak
Kumar McMillan
Lars Francke
Marc Abramowitz
Mike Hommey
Miki Tebeka
Philip Jenvey
Raul Leal
Ronny Pfannschmidt
Stefano Rivera
Tarek Ziadé

View File

@ -1,9 +1,11 @@
recursive-include docs *.txt
recursive-include bin *
recursive-include docs *
recursive-include scripts *
recursive-include virtualenv_support *.egg *.tar.gz
recursive-include virtualenv_embedded *
recursive-exclude docs/_templates *.*
recursive-exclude docs/_templates *
recursive-exclude docs/_build *
include virtualenv_support/__init__.py
include *.py
include AUTHORS.txt
include LICENSE.txt
include LICENSE.txt

View File

@ -17,8 +17,8 @@ Installation
------------
You can install virtualenv with ``pip install virtualenv``, or the `latest
development version <https://github.com/pypa/virtualenv/tarball/develop#egg=virtualenv-dev>`_
with ``pip install virtualenv==dev``.
development version <https://github.com/pypa/virtualenv/tarball/develop>`_
with ``pip install https://github.com/pypa/virtualenv/tarball/develop``.
You can also use ``easy_install``, or if you have no Python package manager
available at all, you can just grab the single file `virtualenv.py`_ and run
@ -52,6 +52,9 @@ environment that has its own installation directories, that doesn't
share libraries with other virtualenv environments (and optionally
doesn't access the globally installed libraries either).
Usage
-----
The basic usage is::
$ python virtualenv.py ENV
@ -71,12 +74,116 @@ Distribute instead of setuptools, just call virtualenv like this::
$ python virtualenv.py --distribute ENV
You can also set the environment variable VIRTUALENV_USE_DISTRIBUTE.
You can also set the environment variable VIRTUALENV_DISTRIBUTE.
A new virtualenv also includes the `pip <http://pypy.python.org/pypi/pip>`_
A new virtualenv also includes the `pip <http://pypi.python.org/pypi/pip>`_
installer, so you can use ``ENV/bin/pip`` to install additional packages into
the environment.
activate script
~~~~~~~~~~~~~~~
In a newly created virtualenv there will be a ``bin/activate`` shell
script. For Windows systems, activation scripts are provided for CMD.exe
and Powershell.
On Posix systems you can do::
$ source bin/activate
This will change your ``$PATH`` so its first entry is the virtualenv's
``bin/`` directory. (You have to use ``source`` because it changes your
shell environment in-place.) This is all it does; it's purely a
convenience. If you directly run a script or the python interpreter
from the virtualenv's ``bin/`` directory (e.g. ``path/to/env/bin/pip``
or ``/path/to/env/bin/python script.py``) there's no need for
activation.
After activating an environment you can use the function ``deactivate`` to
undo the changes to your ``$PATH``.
The ``activate`` script will also modify your shell prompt to indicate
which environment is currently active. You can disable this behavior,
which can be useful if you have your own custom prompt that already
displays the active environment name. To do so, set the
``VIRTUAL_ENV_DISABLE_PROMPT`` environment variable to any non-empty
value before running the ``activate`` script.
On Windows you just do::
> \path\to\env\Scripts\activate
And type `deactivate` to undo the changes.
Based on your active shell (CMD.exe or Powershell.exe), Windows will use
either activate.bat or activate.ps1 (as appropriate) to activate the
virtual environment. If using Powershell, see the notes about code signing
below.
.. note::
If using Powershell, the ``activate`` script is subject to the
`execution policies`_ on the system. By default on Windows 7, the system's
excution policy is set to ``Restricted``, meaning no scripts like the
``activate`` script are allowed to be executed. But that can't stop us
from changing that slightly to allow it to be executed.
In order to use the script, you have to relax your system's execution
policy to ``AllSigned``, meaning all scripts on the system must be
digitally signed to be executed. Since the virtualenv activation
script is signed by one of the authors (Jannis Leidel) this level of
the execution policy suffices. As an administrator run::
PS C:\> Set-ExecutionPolicy AllSigned
Then you'll be asked to trust the signer, when executing the script.
You will be prompted with the following::
PS C:\> virtualenv .\foo
New python executable in C:\foo\Scripts\python.exe
Installing setuptools................done.
Installing pip...................done.
PS C:\> .\foo\scripts\activate
Do you want to run software from this untrusted publisher?
File C:\foo\scripts\activate.ps1 is published by E=jannis@leidel.info,
CN=Jannis Leidel, L=Berlin, S=Berlin, C=DE, Description=581796-Gh7xfJxkxQSIO4E0
and is not trusted on your system. Only run scripts from trusted publishers.
[V] Never run [D] Do not run [R] Run once [A] Always run [?] Help
(default is "D"):A
(foo) PS C:\>
If you select ``[A] Always Run``, the certificate will be added to the
Trusted Publishers of your user account, and will be trusted in this
user's context henceforth. If you select ``[R] Run Once``, the script will
be run, but you will be prometed on a subsequent invocation. Advanced users
can add the signer's certificate to the Trusted Publishers of the Computer
account to apply to all users (though this technique is out of scope of this
document).
Alternatively, you may relax the system execution policy to allow running
of local scripts without verifying the code signature using the following::
PS C:\> Set-ExecutionPolicy RemoteSigned
Since the ``activate.ps1`` script is generated locally for each virtualenv,
it is not considered a remote script and can then be executed.
.. _`execution policies`: http://technet.microsoft.com/en-us/library/dd347641.aspx
The ``--system-site-packages`` Option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you build with ``virtualenv --system-site-packages ENV``, your virtual
environment will inherit packages from ``/usr/lib/python2.7/site-packages``
(or wherever your global site-packages directory is).
This can be used if you have control over the global site-packages directory,
and you want to depend on the packages there. If you want isolation from the
global system, do not use this flag.
Environment variables and configuration files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -93,7 +200,7 @@ virtualenv can not only be configured by passing command line options such as
For example, to automatically install Distribute instead of setuptools
you can also set an environment variable::
$ export VIRTUALENV_USE_DISTRIBUTE=true
$ export VIRTUALENV_DISTRIBUTE=true
$ python virtualenv.py ENV
It's the same as passing the option to virtualenv directly::
@ -114,7 +221,7 @@ virtualenv can not only be configured by passing command line options such as
virtualenv also looks for a standard ini config file. On Unix and Mac OS X
that's ``$HOME/.virtualenv/virtualenv.ini`` and on Windows, it's
``%HOME%\\virtualenv\\virtualenv.ini``.
``%APPDATA%\virtualenv\virtualenv.ini``.
The names of the settings are derived from the long command line option,
e.g. the option ``--distribute`` would look like this::
@ -225,109 +332,9 @@ Here's a more concrete example of how you could use this::
Another example is available `here
<https://github.com/socialplanning/fassembler/blob/master/fassembler/create-venv-script.py>`_.
activate script
~~~~~~~~~~~~~~~
In a newly created virtualenv there will be a ``bin/activate`` shell
script. For Windows systems, activation scripts are provided for CMD.exe
and Powershell.
On Posix systems you can do::
$ source bin/activate
This will change your ``$PATH`` to point to the virtualenv's ``bin/``
directory. (You have to use ``source`` because it changes your shell
environment in-place.) This is all it does; it's purely a convenience. If
you directly run a script or the python interpreter from the virtualenv's
``bin/`` directory (e.g. ``path/to/env/bin/pip`` or
``/path/to/env/bin/python script.py``) there's no need for activation.
After activating an environment you can use the function ``deactivate`` to
undo the changes to your ``$PATH``.
The ``activate`` script will also modify your shell prompt to indicate
which environment is currently active. You can disable this behavior,
which can be useful if you have your own custom prompt that already
displays the active environment name. To do so, set the
``VIRTUAL_ENV_DISABLE_PROMPT`` environment variable to any non-empty
value before running the ``activate`` script.
On Windows you just do::
> \path\to\env\Scripts\activate
And type `deactivate` to undo the changes.
Based on your active shell (CMD.exe or Powershell.exe), Windows will use
either activate.bat or activate.ps1 (as appropriate) to activate the
virtual environment. If using Powershell, see the notes about code signing
below.
.. note::
If using Powershell, the ``activate`` script is subject to the
`execution policies`_ on the system. By default on Windows 7, the system's
excution policy is set to ``Restricted``, meaning no scripts like the
``activate`` script are allowed to be executed. But that can't stop us
from changing that slightly to allow it to be executed.
In order to use the script, you have to relax your system's execution
policy to ``AllSigned``, meaning all scripts on the system must be
digitally signed to be executed. Since the virtualenv activation
script is signed by one of the authors (Jannis Leidel) this level of
the execution policy suffices. As an adminstrator run::
PS C:\> Set-ExecutionPolicy AllSigned
Then you'll be asked to trust the signer, when executing the script.
You will be prompted with the following::
PS C:\> virtualenv .\foo
New python executable in C:\foo\Scripts\python.exe
Installing setuptools................done.
Installing pip...................done.
PS C:\> .\foo\scripts\activate
Do you want to run software from this untrusted publisher?
File C:\foo\scripts\activate.ps1 is published by E=jannis@leidel.info,
CN=Jannis Leidel, L=Berlin, S=Berlin, C=DE, Description=581796-Gh7xfJxkxQSIO4E0
and is not trusted on your system. Only run scripts from trusted publishers.
[V] Never run [D] Do not run [R] Run once [A] Always run [?] Help
(default is "D"):A
(foo) PS C:\>
If you select ``[A] Always Run``, the certificate will be added to the
Trusted Publishers of your user account, and will be trusted in this
user's context henceforth. If you select ``[R] Run Once``, the script will
be run, but you will be prometed on a subsequent invocation. Advanced users
can add the signer's certificate to the Trusted Publishers of the Computer
account to apply to all users (though this technique is out of scope of this
document).
Alternatively, you may relax the system execution policy to allow running
of local scripts without verifying the code signature using the following::
PS C:\> Set-ExecutionPolicy RemoteSigned
Since the ``activate.ps1`` script is generated locally for each virtualenv,
it is not considered a remote script and can then be executed.
.. _`execution policies`: http://technet.microsoft.com/en-us/library/dd347641.aspx
The ``--system-site-packages`` Option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you build with ``virtualenv --system-site-packages ENV``, your virtual
environment will inherit packages from ``/usr/lib/python2.7/site-packages``
(or wherever your global site-packages directory is).
This can be used if you have control over the global site-packages directory,
and you want to depend on the packages there. If you want isolation from the
global system, do not use this flag.
Using Virtualenv without ``bin/python``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
---------------------------------------
Sometimes you can't or don't want to use the Python interpreter
created by the virtualenv. For instance, in a `mod_python
@ -353,7 +360,7 @@ request; you should activate *one* environment as early as possible, and not
do it again in that process.
Making Environments Relocatable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-------------------------------
Note: this option is somewhat experimental, and there are probably
caveats that have not yet been identified. Also this does not
@ -390,8 +397,8 @@ layout).
If you use this flag to create an environment, currently, the
``--system-site-packages`` option will be implied.
The ``--extra-search-dir`` Option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``--extra-search-dir`` option
---------------------------------
When it creates a new environment, virtualenv installs either
setuptools or distribute, and pip. In normal operation, the latest
@ -479,13 +486,15 @@ Contributing
------------
Refer to the `contributing to pip`_ documentation - it applies equally to
virtualenv.
virtualenv, except that virtualenv issues should filed on the `virtualenv
repo`_ at GitHub.
Virtualenv's release schedule is tied to pip's -- each time there's a new pip
release, there will be a new virtualenv release that bundles the new version of
pip.
.. _contributing to pip: http://www.pip-installer.org/en/latest/contributing.html
.. _virtualenv repo: https://github.com/pypa/virtualenv/
Running the tests
~~~~~~~~~~~~~~~~~
@ -537,7 +546,7 @@ Other Documentation and Links
<http://code.google.com/p/modwsgi/wiki/VirtualEnvironments>`_.
* `virtualenv commands
<http://thisismedium.com/tech/extending-virtualenv/>`_ for some more
<https://github.com/thisismedium/virtualenv-commands>`_ for some more
workflow-related tools around virtualenv.
Status and License

View File

@ -1,6 +1,49 @@
Changes & News
--------------
.. warning::
Python bugfix releases 2.6.8, 2.7.3, 3.1.5 and 3.2.3 include a change that
will cause "import random" to fail with "cannot import name urandom" on any
virtualenv created on a Unix host with an earlier release of Python
2.6/2.7/3.1/3.2, if the underlying system Python is upgraded. This is due to
the fact that a virtualenv uses the system Python's standard library but
contains its own copy of the Python interpreter, so an upgrade to the system
Python results in a mismatch between the version of the Python interpreter
and the version of the standard library. It can be fixed by removing
``$ENV/bin/python`` and re-running virtualenv on the same target directory
with the upgraded Python.
1.7.2 (2012-06-22)
~~~~~~~~~~~~~~~~~~
* Updated to distribute 0.6.27.
* Fix activate.fish on OS X. Fixes #8. Thanks David Schoonover.
* Create a virtualenv-x.x script with the Python version when installing, so
virtualenv for multiple Python versions can be installed to the same
script location. Thanks Miki Tebeka.
* Restored ability to create a virtualenv with a path longer than 78
characters, without breaking creation of virtualenvs with non-ASCII paths.
Thanks, Bradley Ayers.
* Added ability to create virtualenvs without having installed Apple's
developers tools (using an own implementation of ``install_name_tool``).
Thanks Mike Hommey.
* Fixed PyPy and Jython support on Windows. Thanks Konstantin Zemlyak.
* Added pydoc script to ease use. Thanks Marc Abramowitz. Fixes #149.
* Fixed creating a bootstrap script on Python 3. Thanks Raul Leal. Fixes #280.
* Fixed inconsistency when having set the ``PYTHONDONTWRITEBYTECODE`` env var
with the --distribute option or the ``VIRTUALENV_USE_DISTRIBUTE`` env var.
``VIRTUALENV_USE_DISTRIBUTE`` is now considered again as a legacy alias.
1.7.1.2 (2012-02-17)
~~~~~~~~~~~~~~~~~~~~
@ -210,7 +253,7 @@ Changes & News
* Fix problem with ``virtualenv --relocate`` when ``bin/`` has
subdirectories (e.g., ``bin/.svn/``); from Alan Franzoni.
* If you set ``$VIRTUALENV_USE_DISTRIBUTE`` then virtualenv will use
* If you set ``$VIRTUALENV_DISTRIBUTE`` then virtualenv will use
Distribute by default (so you don't have to remember to use
``--distribute``).
@ -301,7 +344,7 @@ Changes & News
``distutils.cfg`` -- this has been causing problems for a lot of
people, in rather obscure ways.
* If you use a `boot script <./index.html#boot-script>`_ it will attempt to import ``virtualenv``
* If you use a boot script it will attempt to import ``virtualenv``
and find a pre-downloaded Setuptools egg using that.
* Added platform-specific paths, like ``/usr/lib/pythonX.Y/plat-linux2``

View File

@ -1,16 +1,31 @@
import sys, os
import os
import re
import shutil
import sys
try:
from setuptools import setup
kw = {'entry_points':
"""[console_scripts]\nvirtualenv = virtualenv:main\n""",
'zip_safe': False}
setup_params = {
'entry_points': {
'console_scripts': [
'virtualenv=virtualenv:main',
'virtualenv-%s.%s=virtualenv:main' % sys.version_info[:2]
],
},
'zip_safe': False,
'test_suite': 'nose.collector',
'tests_require': ['nose', 'Mock'],
}
except ImportError:
from distutils.core import setup
if sys.platform == 'win32':
print('Note: without Setuptools installed you will have to use "python -m virtualenv ENV"')
kw = {}
setup_params = {}
else:
kw = {'scripts': ['scripts/virtualenv']}
script = 'scripts/virtualenv'
script_ver = script + '-%s.%s' % sys.version_info[:2]
shutil.copy(script, script_ver)
setup_params = {'scripts': [script, script_ver]}
here = os.path.dirname(os.path.abspath(__file__))
@ -23,13 +38,35 @@ f = open(os.path.join(here, 'docs', 'news.txt'))
long_description += "\n\n" + f.read()
f.close()
setup(name='virtualenv',
# If you change the version here, change it in virtualenv.py and
# docs/conf.py as well
version="1.7.1.2",
description="Virtual Python Environment builder",
long_description=long_description,
classifiers=[
def get_version():
f = open(os.path.join(here, 'virtualenv.py'))
version_file = f.read()
f.close()
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
version_file, re.M)
if version_match:
return version_match.group(1)
raise RuntimeError("Unable to find version string.")
# Hack to prevent stupid TypeError: 'NoneType' object is not callable error on
# exit of python setup.py test # in multiprocessing/util.py _exit_function when
# running python setup.py test (see
# http://www.eby-sarna.com/pipermail/peak/2010-May/003357.html)
try:
import multiprocessing
except ImportError:
pass
setup(
name='virtualenv',
# If you change the version here, change it in virtualenv.py and
# docs/conf.py as well
version=get_version(),
description="Virtual Python Environment builder",
long_description=long_description,
classifiers=[
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
@ -41,18 +78,15 @@ setup(name='virtualenv',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.1',
'Programming Language :: Python :: 3.2',
],
keywords='setuptools deployment installation distutils',
author='Ian Bicking',
author_email='ianb@colorstudy.com',
maintainer='Jannis Leidel, Carl Meyer and Brian Rosner',
maintainer_email='python-virtualenv@groups.google.com',
url='http://www.virtualenv.org',
license='MIT',
py_modules=['virtualenv'],
packages=['virtualenv_support'],
package_data={'virtualenv_support': ['*-py%s.egg' % sys.version[:3], '*.tar.gz']},
test_suite='nose.collector',
tests_require=['nose', 'Mock'],
**kw
)
],
keywords='setuptools deployment installation distutils',
author='Ian Bicking',
author_email='ianb@colorstudy.com',
maintainer='Jannis Leidel, Carl Meyer and Brian Rosner',
maintainer_email='python-virtualenv@groups.google.com',
url='http://www.virtualenv.org',
license='MIT',
py_modules=['virtualenv'],
packages=['virtualenv_support'],
package_data={'virtualenv_support': ['*-py%s.egg' % sys.version[:3], '*.tar.gz']},
**setup_params)

View File

@ -4,11 +4,13 @@
# If you change the version here, change it in setup.py
# and docs/conf.py as well.
virtualenv_version = "1.7.1.2"
__version__ = "1.7.2" # following best practices
virtualenv_version = __version__ # legacy, again
import base64
import sys
import os
import codecs
import optparse
import re
import shutil
@ -18,6 +20,7 @@ import zlib
import errno
import distutils.sysconfig
from distutils.util import strtobool
import struct
try:
import subprocess
@ -443,7 +446,7 @@ def writefile(dest, content, overwrite=True):
f = open(dest, 'rb')
c = f.read()
f.close()
if c != content:
if c != content.encode("utf-8"):
if not overwrite:
logger.notify('File %s exists with different content; not overwriting', dest)
return
@ -487,7 +490,7 @@ def _install_req(py_executable, unzip=False, distribute=False,
source = None
else:
setup_fn = None
source = 'distribute-0.6.24.tar.gz'
source = 'distribute-0.6.27.tar.gz'
project_name = 'distribute'
bootstrap_script = DISTRIBUTE_SETUP_PY
@ -568,6 +571,8 @@ def _install_req(py_executable, unzip=False, distribute=False,
finally:
logger.indent -= 2
logger.end_progress()
if cwd is not None:
shutil.rmtree(cwd)
if os.getcwd() != old_chdir:
os.chdir(old_chdir)
if is_jython and os._name == 'nt':
@ -616,9 +621,18 @@ def install_pip(py_executable, search_dirs=None, never_download=False):
easy_install_script = 'easy_install'
if sys.platform == 'win32':
easy_install_script = 'easy_install-script.py'
cmd = [join(os.path.dirname(py_executable), easy_install_script), filename]
if sys.platform == 'win32':
cmd.insert(0, py_executable)
# There's two subtle issues here when invoking easy_install.
# 1. On unix-like systems the easy_install script can *only* be executed
# directly if its full filesystem path is no longer than 78 characters.
# 2. A work around to [1] is to use the `python path/to/easy_install foo`
# pattern, but that breaks if the path contains non-ASCII characters, as
# you can't put the file encoding declaration before the shebang line.
# The solution is to use Python's -x flag to skip the first line of the
# script (and any ASCII decoding errors that may have occurred in that line)
cmd = [py_executable, '-x', join(os.path.dirname(py_executable), easy_install_script), filename]
# jython and pypy don't yet support -x
if is_jython or is_pypy:
cmd.remove('-x')
if filename == 'pip':
if never_download:
logger.fatal("Can't find any local distributions of pip to install "
@ -754,7 +768,7 @@ class ConfigOptionParser(optparse.OptionParser):
# Old, pre-Optik 1.5 behaviour.
return optparse.Values(self.defaults)
defaults = self.update_defaults(self.defaults.copy()) # ours
defaults = self.update_defaults(self.defaults.copy()) # ours
for option in self._get_all_options():
default = defaults.get(option.dest)
if isinstance(default, basestring):
@ -797,12 +811,13 @@ def main():
action='store_true',
help="Clear out the non-root install and start from scratch")
parser.set_defaults(system_site_packages=False)
parser.add_option(
'--no-site-packages',
dest='no_site_packages',
action='store_true',
dest='system_site_packages',
action='store_false',
help="Don't give access to the global site-packages dir to the "
"virtual environment")
"virtual environment (default)")
parser.add_option(
'--system-site-packages',
@ -825,7 +840,7 @@ def main():
'This fixes up scripts and makes all .pth files relative')
parser.add_option(
'--distribute',
'--distribute', '--use-distribute', # the second option is for legacy reasons here. Hi Kenneth!
dest='use_distribute',
action='store_true',
help='Use Distribute instead of Setuptools. Set environ variable '
@ -848,7 +863,7 @@ def main():
"if local distributions of setuptools/distribute/pip are not present.")
parser.add_option(
'--prompt=',
'--prompt',
dest='prompt',
help='Provides an alternative prompt prefix for this environment')
@ -863,7 +878,7 @@ def main():
adjust_options(options, args)
verbosity = options.verbose - options.quiet
logger = Logger([(Logger.level_for_integer(2-verbosity), sys.stdout)])
logger = Logger([(Logger.level_for_integer(2 - verbosity), sys.stdout)])
if options.python and not os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'):
env = os.environ.copy()
@ -914,10 +929,6 @@ def main():
make_environment_relocatable(home_dir)
return
if options.no_site_packages:
logger.warn('The --no-site-packages flag is deprecated; it is now '
'the default behavior.')
create_environment(home_dir,
site_packages=options.system_site_packages,
clear=options.clear,
@ -1030,9 +1041,7 @@ def create_environment(home_dir, site_packages=False, clear=False,
install_distutils(home_dir)
# use_distribute also is True if VIRTUALENV_DISTRIBUTE env var is set
# we also check VIRTUALENV_USE_DISTRIBUTE for backwards compatibility
if use_distribute or os.environ.get('VIRTUALENV_USE_DISTRIBUTE'):
if use_distribute:
install_distribute(py_executable, unzip=unzip_setuptools,
search_dirs=search_dirs, never_download=never_download)
else:
@ -1065,7 +1074,7 @@ def path_locations(home_dir):
lib_dir = join(home_dir, 'Lib')
inc_dir = join(home_dir, 'Include')
bin_dir = join(home_dir, 'Scripts')
elif is_jython:
if is_jython:
lib_dir = join(home_dir, 'Lib')
inc_dir = join(home_dir, 'Include')
bin_dir = join(home_dir, 'bin')
@ -1073,7 +1082,7 @@ def path_locations(home_dir):
lib_dir = home_dir
inc_dir = join(home_dir, 'include')
bin_dir = join(home_dir, 'bin')
else:
elif sys.platform != 'win32':
lib_dir = join(home_dir, 'lib', py_version)
inc_dir = join(home_dir, 'include', py_version + abiflags)
bin_dir = join(home_dir, 'bin')
@ -1096,8 +1105,9 @@ def change_prefix(filename, dst_prefix):
for src_prefix in prefixes:
if filename.startswith(src_prefix):
_, relpath = filename.split(src_prefix, 1)
assert relpath[0] == os.sep
relpath = relpath[1:]
if src_prefix != os.sep: # sys.prefix == "/"
assert relpath[0] == os.sep
relpath = relpath[1:]
return join(dst_prefix, relpath)
assert False, "Filename %s does not start with any of these prefixes: %s" % \
(filename, prefixes)
@ -1250,7 +1260,7 @@ def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear):
if os.path.exists(pyd_pth):
logger.info('Deleting %s (not Windows env or not build directory python)' % pyd_pth)
os.unlink(pyd_pth)
if sys.executable != py_executable:
## FIXME: could I just hard link?
executable = sys.executable
@ -1295,9 +1305,17 @@ def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear):
if is_pypy:
# make a symlink python --> pypy-c
python_executable = os.path.join(os.path.dirname(py_executable), 'python')
if sys.platform in ('win32', 'cygwin'):
python_executable += '.exe'
logger.info('Also created executable %s' % python_executable)
copyfile(py_executable, python_executable)
if sys.platform == 'win32':
for name in 'libexpat.dll', 'libpypy.dll', 'libpypy-c.dll', 'libeay32.dll', 'ssleay32.dll', 'sqlite.dll':
src = join(prefix, name)
if os.path.exists(src):
copyfile(src, join(bin_dir, name))
if os.path.splitext(os.path.basename(py_executable))[0] != expected_exe:
secondary_exe = os.path.join(os.path.dirname(py_executable),
expected_exe)
@ -1340,15 +1358,23 @@ def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear):
# And then change the install_name of the copied python executable
try:
call_subprocess(
["install_name_tool", "-change",
os.path.join(prefix, 'Python'),
'@executable_path/../.Python',
py_executable])
mach_o_change(py_executable,
os.path.join(prefix, 'Python'),
'@executable_path/../.Python')
except:
logger.fatal(
"Could not call install_name_tool -- you must have Apple's development tools installed")
raise
e = sys.exc_info()[1]
logger.warn("Could not call mach_o_change: %s. "
"Trying to call install_name_tool instead." % e)
try:
call_subprocess(
["install_name_tool", "-change",
os.path.join(prefix, 'Python'),
'@executable_path/../.Python',
py_executable])
except:
logger.fatal("Could not call install_name_tool -- you must "
"have Apple's development tools installed")
raise
# Some tools depend on pythonX.Y being present
py_executable_version = '%s.%s' % (
@ -1372,17 +1398,9 @@ def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear):
# argument that has a space in it. Instead we have to quote
# the value:
py_executable = '"%s"' % py_executable
cmd = [py_executable, '-c', """
import sys
prefix = sys.prefix
if sys.version_info[0] == 3:
prefix = prefix.encode('utf8')
if hasattr(sys.stdout, 'detach'):
sys.stdout = sys.stdout.detach()
elif hasattr(sys.stdout, 'buffer'):
sys.stdout = sys.stdout.buffer
sys.stdout.write(prefix)
"""]
# NOTE: keep this check as one line, cmd.exe doesn't cope with line breaks
cmd = [py_executable, '-c', 'import sys;out=sys.stdout;'
'getattr(out, "buffer", out).write(sys.prefix.encode("utf-8"))']
logger.info('Testing executable with %s %s "%s"' % tuple(cmd))
try:
proc = subprocess.Popen(cmd,
@ -1394,7 +1412,7 @@ sys.stdout.write(prefix)
logger.fatal('ERROR: The executable %s could not be run: %s' % (py_executable, e))
sys.exit(100)
else:
raise e
raise e
proc_stdout = proc_stdout.strip().decode("utf-8")
proc_stdout = os.path.normcase(os.path.abspath(proc_stdout))
@ -1430,6 +1448,7 @@ sys.stdout.write(prefix)
return py_executable
def install_activate(home_dir, bin_dir, prompt=None):
home_dir = os.path.abspath(home_dir)
if sys.platform == 'win32' or is_jython and os._name == 'nt':
@ -1444,7 +1463,7 @@ def install_activate(home_dir, bin_dir, prompt=None):
home_dir_msys = (drive and "/%s%s" or "%s%s") % (drive[:1], tail)
# Run-time conditional enables (basic) Cygwin compatibility
home_dir_sh = ("""$(if [ "$OSTYPE" "==" "cygwin" ]; then cygpath -u '%s'; else echo '%s'; fi;)""" %
home_dir_sh = ("""$(if [ "$OSTYPE" "==" "cygwin" ]; then cygpath -u '%s'; else echo '%s'; fi;)""" %
(home_dir, home_dir_msys))
files['activate'] = ACTIVATE_SH.replace('__VIRTUAL_ENV__', home_dir_sh)
@ -1658,7 +1677,7 @@ def fixup_pth_file(filename):
def fixup_egg_link(filename):
f = open(filename)
link = f.read().strip()
link = f.readline().strip()
f.close()
if os.path.abspath(link) != link:
logger.debug('Link in %s already relative' % filename)
@ -1750,7 +1769,7 @@ def create_bootstrap_script(extra_text, python_version=''):
filename = __file__
if filename.endswith('.pyc'):
filename = filename[:-1]
f = open(filename, 'rb')
f = codecs.open(filename, 'r', encoding='utf-8')
content = f.read()
f.close()
py_exe = 'python%s' % python_version
@ -1852,57 +1871,57 @@ TpQY7vfqZYGrJFUu2hFOm2GZWvwYWRnWwiwrs3anDVLYbUMUR5lhlpieV1RL6vME8DI9TTgkglgJ
z0mYDDxv6KZ5bYoHs3QOehRULijUCQgJEhcPAxLnFTnnwItKmTNE8LDcVunVqsZ9Bugc0/kFbP7j
8eez0/dU0//iZet1DzDnhCKBCddzHGG1HmY74ItbgYdcNZ0O8ax+hTBQ+8Cf7isuFDniAXr9OLGI
f7qv+BDXkRMJ8gxAQTVlVzwwAHC6DclNKwuMq42D8eNW47WY+WAoF4lnRnTNhTu/Pifalh1TQnkf
8/IRGzjLUtMwMp3d6rDuR89xWeKO0yIabgRvh2TLfGbQ9br3ZlcdmvvpSSGeJwWM+q39MUyhVq+p
no7DbLu4hcJabWN/yZ1cqdNunqMoAxEjt/PYZbJhJaybMwd6Fc09YOJbja6RxEFVPvolH2kPw8PE
ErsXp5iOdKKEjABmMqQ+ONOAD4UWARQIFeJGjuROxk9feHN0rMH9c9S6C2zjD6AIdVksHbcoKuBE
+PIbO478itBCPXooQsdTyWVe2JIt/GxW6FU+9+c4KAOUxESxq5L8SkYMa2JgfuUTe0cKlrStR+qL
9HLIsIhTcE5vd3B4Xy6GN04Mah1G6LW7ltuuOvLJgw0GT2XcSTAffJVsQPeXTR3xSg6L/PBBtN1Q
74eIhYDQVO+DRyGmY34Ld6xPC3iQGhoWeni/7diF5bUxjqy1j50DRqF9oT3YeQWhWa1oW8Y52Wd8
UesFtAb3qDX5I/tU1+zY3wNHtpyckAXKg7sgvbmNdINOOmHEJ4f42GVKlentwRb9biFvZFaA6wVR
HR48+NUePBjHNp0yWJL1xdidb8+3w7jRmxazQ3MyAj0zVcL6xbmsDxCdwYzPXZi1yOBS/6JDkiS/
Ji/5zd9PJ+LN+5/g39fyA8RVeHJwIv4BaIg3RQXxJR99pTsJ8FBFzYFj0Sg8XkjQaKuCr29At+3c
ozNui+jTHv4xD6spBRa4Vmu+MwRQ5AnScfDWTzBnGOC3OWTV8UaNpzi0KCP9Emmw+9wJntU40C3j
Vb3O0F44WZJ2NS9GZ6dvTt5/PInrW+Rw83PkZFH82iicjt4jrnA/bCLsk3mDTy4dx/kHmZUDfrMO
Os0ZFgw6RQhxSWkDTb6PIrHBRVJh5kCU20Uxj7ElsDwfm6s34EiPnfjyXkPvWVmEFY31LlrrzeNj
oIb4pauIRtCQ+ug5UU9CKJnh+S1+HI+GTfFEUGob/jy93izczLg+iEMT7GLazjryu1tduGI6a3iW
kwivI7sM5mxmliZqPZu7Z/Y+5EJfJwJajvY55DJpslrIHCSXgny61wE0vXvMjiWEWYXNGZ09ozRN
tkm2yilCSpQY4agjOpqOGzKUMYQY/Mfkmu0Bnv8TDR8kBuiEKMVPhdNVNfMVSzCHRES9gcKDTZq/
dOt5NIV5UI6Q560jC/NEt5ExupK1nj8/iMYXz9tKB8pKz71DtvMSrJ7LJnugOsunT5+OxH/c7/0w
KnFWFNfglgHsQa/ljF7vsNx6cna1+p69eRMDP85X8gIeXFL23D5vckpN3tGVFkTavwZGiGsTWmY0
7Tt2mZN2FW80cwvesNKW4+c8pUuDMLUkUdnqu5cw7WSkiVgSFEOYqHmahpymgPXYFg2ej8M0o+YX
eQscnyKYCb7FHTIOtVfoYVItq+Uei86RGBHgEdWW8Wh0wJhOiAGe0/OtRnN6mqd1e7Tjmbt5qg/S
1/YuIM1XItmgZJh5dIjhHLX0WLX1sIs7WdSLWIr5hZtw7MySX9+HO7A2SFqxXBpM4aFZpHkhq7kx
p7hi6TytHTCmHcLhznQFElmfOBhAaQTqnazCwkq0ffsnuy4uph9oH3nfjKTLh2p7rRQnh5K8U2AY
x+34lIayhLR8a76MYZT3lNbWnoA3lviTTqpiXb93+4V7xLDJ9a0WXL/RXnUBcOgmJasgLTt6OsK5
vsvCZ6bdcRcFfihEJ9xu0qpukmyqL0+YosM2tRvrGk97NO3OQ5fWWwEnvwAPeF9X0YPjYKpskJ5Y
BGtOSRyJpU5RxO5pL/9gVFmgl/eCfSXwKZAyi6k5o2ySSBeWXe3hT12z6ah4BPWVOVD0EJtgjrX0
ToS405hQ0VM47la59lrhBos5tmA9725k8KghO7B8L95MsHunhfjuSETPJ+LPnUBsXm7xViYgw5NF
/GQR+j4hdb04fNHauX7g24GwE8jLy0dPN0tnNL1wqPz8/r666BED0DXI7jKVi/0nCrFjnL8UqobS
zms3p9KM8XT6nq260gez2+MqdCptBlHFplVojmoz/q8dxJz41nqID8ei0mALaA/0m8KXTvGhvXQN
CxM1ev7KopRMhzbH8BtenALvNUFdodq5aaor7C3YgZyAPkbJW2Btw4Gg8BE8FNIlL7RoX3W2hf/I
xeOi/V2biz0sv/n6LjxdAR88sTBAUI+YTqs/kKl2ssxjF+YB+/X389/Dee8uvns0lXSvYVphKIWF
zKuE36BJbMpDm2owIolbQZFb3oaf+nrwTAyLI+qm+jq8a/rc/6656xaBnbnZ3e3N3T/75tJA993N
L0M04DBPE+JBNeOtwA7rAleMJ7qoYDhlqT9IfrcTznSHVrgPjClhwAQosanG3mjNdTJ3v2OFzD5f
7+oedRzU1Z1p985+djn+IYqWqwHwuT39TCUeC82B7DfSfV1TLhqcyqsrNU3wrrgpBRtU4NLzIo37
+o6u+pKJ2hqvEy9UARCGm3QpolttDIwBAQ3fWcv1Ic7NGYKGpipKpyxTpQvOIGkXF8DFnDmi/iYz
yXWVo0xiwk81VVlBVDDSN5ty4cJQrWcL1CQy1om6NqibHhN90SUOwdUy5ngk56s40vCoA4TgU1PO
tU1cqDyd2nfAL8/aY+DpxDKEzJu1rJK6vQLF3yZNxXfOCHQoFhfYSVW0ktnhFBex1PKHgxQmC+z3
r7ST7QUZd5z9Hlut93C2oh46BfaYY+WO7THcnN7aK9Dcq3cWdGGua+Rts5b77LUvsBTmPi/SlTp3
wG/1HUN8cyVnNtFNcPgI5N49kuaX51q1xk6KRcN55iqG/qUyeKqZbPHQXXE9LujfCtdx9O34vt6w
zNILDXY0tlTUrtWg4mlHG7cRNVbS3RNR+9XSj4yoPfgPjKj1zX5gcDQ+Wh8M1k/fE3qzmnCvyWsZ
AfpMgUi4s9e5ZM2YzMitRoawN70d2WtqWWc6R5yMmUCO7N+fRCD4Ojzllm5611WZcYciWl+66PH3
Zx9eH58RLabnx2/+8/h7qlbB9HHHZj045ZAX+0ztfa8u1k0/6AqDocFbbAfuneTDHRpC731vc3YA
wvBBnqEF7Soy9/WuDr0DEf1OgPjd0+5A3aWyByH3/DNdfO/WFXQKWAP9lKsNzS9ny9Y8MjsXLA7t
zoR53yaTtYz2cm27Fs6p++urE+236psKd+QBx7b6lFYAc8jIXzaFbI4S2EQlOyrd/3kAlcziMSxz
ywdI4Vw6t83RRXMMqvb/LwUVKLsE98HYYZzYG3+pHafLlb3KGvfC5jI2BPHOQY3683OFfSGzHVQI
AlZ4+i41RsToP73BZLdjnyhxsU8nLvdR2VzaX7hm2sn9e4qbrrW9k0hx5QZvO0HjZZO5G6m2T68D
OX+UnS+WTok/aL4DoHMrngrYG30mVoizrQghkNQbhlg1SHTUF4o5yKPddLA3tHom9nedx3PPownx
fHfDRefIm+7xgnuoe3qoxpx6ciwwlq/tOmgnviPIvL0j6BIiz/nAPUV99y18vbl4fmiTrcjv+NpR
JFRmM3IM+4VTpnbnxXdOd2KWakJ1TBizOcc0dYtLByr7BLtinF6t/o44yOz7MqSR9364yMf08C70
HnUxtax3CFMS0RM1pmk5pxs07vbJuD/dVm31gfBJjQcA6alAgIVgerrRqZzbcvlr9ExHhbOGrgx1
M+6hIxVUReNzBPcwvl+LX7c7nbB8UHdG0fTnBl0O1EsOws2+A7caeymR3SahO/WWD3a4AHxYdbj/
8wf079d32e4v7vKrbauXgwek2JfFkkCslOiQyDyOwciA3oxIW2MduRF0vJ+jpaPLUO3ckC/Q8aMy
Q7wQmAIMcman2gOwRiH4P2ts6wE=
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
YAowyJmdag/AGoXg/wBaZusT
""")
##file ez_setup.py
@ -1978,139 +1997,139 @@ BDaonX65d/fwEjNqlDjLVIvM9X+XVxF7
##file distribute_setup.py
DISTRIBUTE_SETUP_PY = convert("""
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
""")
##file activate.sh
ACTIVATE_SH = convert("""
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
9f8c6P8+zPcNcszYX1+tk3g=
""")
##file activate.fish
ACTIVATE_FISH = convert("""
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
""")
##file activate.csh
ACTIVATE_CSH = convert("""
eJx9U11vmzAUffevOCVRu+UB9pws29Kl0iq1aVWllaZlcgxciiViItsQdb9+xiQp+dh4QOB7Pu49
XHqY59IgkwVhVRmLmFAZSrGRNkdgykonhFiqSCRW1sJSmJg8wCDT5QrucRCyHn6WFRKhVGmhKwVp
kUpNiS3emup3TY6XIn7DVNQyJUwlrgthJD6n/iCNv72uhCzCpFx9CRkThRQGKe08cWXJ9db/yh/u
pvzl9mn+PLnjj5P5D1yM8QmXlzBkSdXwZ0H/BBc0mEo5FE5qI2jKhclHOOvy9HD/OO/6YO1mX9vx
sY0H/tPIV0dtqel0V7iZvWyNg8XFcBA0ToEqVeqOdNUEQFvN41SumAv32VtJrakQNSmLWmgp4oJM
yDoBHgoydtoEAs47r5wHHnUal5vbJ8oOI+9wI86vb2d8Nrm/4Xy4RZ8R85E4uTZPB5EZPnTaaAGu
E59J8BE2J8XgrkbLeXMlVoQxznEYFYY8uFFdxsKQRx90Giwx9vSueHP1YNaUSFG4vTaErNSYuBOF
lXiVyXa9Sy3JdClEyK1dD6Nos9mEf8iKlOpmqSNTZnYjNEWiUYn2pKNB3ttcLJ3HmYYXy6Un76f7
r8rRsC1TpTJj7f19m5sUf/V3Ir+x/yjtLu8KjLX/CmN/AcVGUUo=
eJx9U11v2jAUffevOA2o3ZBG9gxjGx2VVqmlVUUrTWMyTnLTWEocZDsg+uvnOEDDx5aHKLn3fFyf
3HQwy6RBKnNCURmLiFAZSrCWNkNgykrHhEiqUMRWroSlfmyyAL1UlwXcY6/POvhVVoiFUqWFrhSk
RSI1xTbf1N0fmhwvQbTBRKxkQphIXOfCSHxJfCGJvr8WQub9uCy+9hkTuRQGCe08cWXJzdb9xh/u
Jvzl9mn2PL7jj+PZT1yM8BmXlzBkSa3ga0H3BBfUmEo5FE56Q2jKhMmGOOvy9HD/OGv7YOnOvrSj
YxsP/KeR7w6bVj3prnEzfdkaB/OLQS+onQJVqsSVdFUHQFvNk1Ra1eUmKeMr5tJ+9t5Sa8rFipTF
SmgpopxMn7W4hw6MnU6FgPPWK+eBR53m54LwEbPDb9Dihpxf3075dHx/w/lgiz4j5jNyck3ADiJT
fGiN0QDcJD6k4CNsRorBXbWW8+ZKFIQRznEY5YY8uFZdRMKQRx9MGiww8vS2eH11YJYUS5G7RTeE
tNQYu4pCIV5lvN33UksybQoRMmuXgzBcr9f9N7IioVW95aEpU7sWmkJRq4R70tFB3secL5zHmYHn
i4Un70/3X5WjwzZMlciUNff39a5T/N3difzB/qM0y71r7H5Wv4DubrNS4VPRvDPW/FmM/QUd6WEa
""")
##file activate.bat
@ -2221,28 +2240,27 @@ QastYw==
##file distutils-init.py
DISTUTILS_INIT = convert("""
eJytV92L4zYQf/dfMU0ottuse7RvC6FQrg8Lxz2Ugz4si9HacqKuIxlJ2ST313dG8odkO9d7aGBB
luZLv/nNjFacOqUtKJMIvzK3cXlhWgp5MDBsqK5SNYftsBAGpLLA4F1oe2Ytl+9wUvW55TswCi4c
KibhbFDSglXQCFmDPXIwtm7FawLRbwtPzg2T9gf4gupKv4GS0N262w7V0NvpbCy8cvTo3eAus6C5
ETU3ICQZX1hFTw/dzR6V/AW1RCN4/XAtbsVXqIXmlVX6liS4lOzEYY9QFB2zx6LfoSNjz1a0pqT9
QOIfJWQ2E888NEVZNqLlZZnvIB0NpHkimlFdKn2iRRY7yGG/CCJb6Iz280d34SFXBS2yEYPNF0Q7
yM7oCjpWvbEDQmnhRwOs6zjThpKE8HogwRAgraqYFZgGZvzmzVh+mgz9vskT3hruwyjdFcqyENJw
bbMPO5jdzonxK68QKT7B57CMRRG5shRSWDTX3dI8LzRndZbnSWL1zfvriUmK4TcGWSnZiEPCrxXv
bM+sP7VW2is2WgWXCO3sAu3Rzysz3FiNCA8WPyM4gb1JAAmCiyTZbhFjWx3h9SzauuRXC9MFoVbc
yNTCm1QXOOIfIn/g1kGMhDUBN72hI5XCBQtIXQw8UEEdma6Jaz4vJIJ51Orc15hzzmu6TdFp3ogr
Aof0c98tsw1SiaiWotHffk3XYCkqdToxWRfTFXqgpg2khcLluOHMVC0zZhLKIomesfSreUNNgbXi
Ky9VRzwzkBneNoGQyyvGjbsFQqOZvpWIjqH281lJ/jireFgR3cPzSyTGWzQpDNIU+03Fs4XKLkhp
/n0uFnuF6VphB44b3uWRneSbBoMSioqE8oeF0JY+qTvYfEK+bPLYdoR4McfYQ7wMZj39q0kfP8q+
FfsymO0GzNlPh644Jje06ulqHpOEQqdJUfoidI2O4CWx4qOglLye6RrFQirpCRXvhoRqXH3sYdVJ
AItvc+VUsLO2v2hVAWrNIfVGtkG351cUMNncbh/WdowtSPtCdkzYFv6mwYc9o2Jt68ud6wectBr8
hYAulPSlgzH44YbV3ikjrulEaNJxt+/H3wZ7bXSXje/YY4tfVVrVmUstaDwwOBLMg6iduDB0lMVC
UyzYx7Ab4kjCqdViEJmDcdk/SKbgsjYXgfMznUWcrtS4z4fmJ/XOM1LPk/iIpqass5XwNbdnLb1Y
8h3ERXSWZI6rZJxKs1LBqVH65w0Oy4ra0CBYxEeuOMbDmV5GI6E0Ha/wgVTtkX0+OXvqsD02CKLf
XHbeft85D7tTCMYy2Njp4DJP7gWJr6paVWXZ1+/6YXLv/iE0M90FktiI7yFJD9e7SOLhEkkaMTUO
azq9i2woBNR0/0eoF1HFMf0H8ChxH/jgcB34GZIz3Qn4/vid+VEamQrOVqAPTrOfmD4MPdVh09tb
8dLLjvh/61lEP4yW5vJaH4vHcevG8agXvzPGoOhhXNncpTr99PTHx6e/UvffFLaxUSjuSeP286Dw
gtEMcW1xKr/he4/6IQ6FUXP+0gkioHY5iwC9Eyx3HKO7af0zPPe+XyLn7fAY78k4aiR387bCr5XT
5C4rFgwLGfMvJuAMew==
eJytV92L4zYQf9dfMU0ottuse7RvC6FQrg8Lxz2Ugz4si9HacqKuIxlJ2ST313fG8odkO9d7qGFB
keZLv/nNjFaeWm0caMukX9nbuLxwo6Q6WBg2dFvqSsB2WEgLSjvg8C6NO/NGqHc46erciB1YDRcB
JVdwtijpwGmoparAHQVYVzXylUH0beGpc8OV+wG+oLo2b6AVtLf2tkM19HY6WwevAj16N7jLHRhh
ZSUsSEXGF1bR00N7c0etfkEtWUtRPVzzW/4VKmlE6bS5MYZLxU8C9ghF3nJ3zPsdOrLu7GRjC9oP
JP7RUqUz8dRDkxdFLRtRFNkOktFAkjFZj+pKmxMt0thBBvtFEOlCZ7SfPXYXHnKV0yIdMdh8QbSD
7IyuoOXlGz8glA5+tMDbVnBjKUkIrwcSLAHS6JI7iWng1m/erBOnydDvm4yJxgofRtFdoShyqaww
Lv2wg9ntOjFxFSUiJSb4OixjUUSuKKSSDs21tyTLciN4lWYZY7XRQQjQ05M2dhRgqVUtD4w5c/Nh
vXIrrDMIEBPXUrQOPuPd/jRGm7kA5hcXjG23CJErj/B6lk1ViKuDKT6otLAqcfCm9AWO+IfAHYTr
EEK+2YBa3tCRmHxB/uuLhQeqhyM3FVHFw0oimAajz32JdM5FRdfJWyNqecV7I3u63w13NTKBmJKg
0d9+Tfw1YlzyUp9OXFX5dIUeqWkDs6pxOW50ZsqGWzsJpZFETzj6KlFTTfNGfhWFbokmFlIrmjoQ
oo/ixt0coTHc3ApEx1L3+KyVeJwVLKyI7uH5JRITDZqUFlmG7aIU6UJlF6Q0+z4Xi73cto10A0Wt
aLPIDvumwaACIo5T/pDHTeGTuoPNJ+TLJottR4jnc4w9xMtg1tO/mvTxR9F3Ul8Gs92AOfvpsCuO
yQ2terraR8YodGr0ha/Crk8RvCSWf5SUktczXSNfSLGeUPFuSKi6q489rDoJYPFdqpgKdta1F50m
QK0+JN7INmjW4ooCNp3b7cPajrEFaV/Ijgnbwt80t7BnlLxpfLkL84CD0oC/ENCFWF86GIOfTVjt
rbbymkyEJp3u9v302mCrjO6y8Q137NCrSqs6c6kFjQcGR4JZEHUnLi0dpbHQFAv2MeyGOFFw6DQY
RNrBuOwfJJMLVdmLxPGXzCJOVmrc58OIk34XKalnLD6ioaeqdCV8I9zZKC/GvoO4iM6SzHGVjGNp
Vio4NQr/OsFZV1IbGgTz+KgrjvFwppfSSChsK0p835R7ZJ9Pzp46bI8Ngug3l5233++ch90pBGMZ
bOx0cJmxe0Hio6jSZVH09bt+yO7dP4RmprtAEhvxPSTp3XkXSTxcIkkjpsJhTad3kQ2FgJru/wj1
Iqo4pv8AHiXuAx8crgM/Q3KmOwHfH79zP0ojU8HZCvTBafoTN4ehp3bY9PZWvPSyI/7fehbRh9HS
XF7rY/E4brpxPOrF74wxKHrXli7rUp18evrj49NfSffPELaxUSjuSeP286DwgtEMcW1xKr/he4/6
IQ6FUXP+0gkioHY5iwC9Eyx3HKO7af0zPPe+XyLnZDUk46jB7uZthV8rp+wuKxYMCxnzL9Vk9s4=
""")
##file distutils.cfg
@ -2266,6 +2284,145 @@ YdN2dEngUlbC4PG60M1WEN0piu7Nq7on0mgyyUw3iV1etLo6r/81biWdQ9MWHFaePWZYaq+nmp+t
s3az+sj7eA0jfgPfeoN1
""")
MH_MAGIC = 0xfeedface
MH_CIGAM = 0xcefaedfe
MH_MAGIC_64 = 0xfeedfacf
MH_CIGAM_64 = 0xcffaedfe
FAT_MAGIC = 0xcafebabe
BIG_ENDIAN = '>'
LITTLE_ENDIAN = '<'
LC_LOAD_DYLIB = 0xc
maxint = majver == 3 and getattr(sys, 'maxsize') or getattr(sys, 'maxint')
class fileview(object):
"""
A proxy for file-like objects that exposes a given view of a file.
Modified from macholib.
"""
def __init__(self, fileobj, start=0, size=maxint):
if isinstance(fileobj, fileview):
self._fileobj = fileobj._fileobj
else:
self._fileobj = fileobj
self._start = start
self._end = start + size
self._pos = 0
def __repr__(self):
return '<fileview [%d, %d] %r>' % (
self._start, self._end, self._fileobj)
def tell(self):
return self._pos
def _checkwindow(self, seekto, op):
if not (self._start <= seekto <= self._end):
raise IOError("%s to offset %d is outside window [%d, %d]" % (
op, seekto, self._start, self._end))
def seek(self, offset, whence=0):
seekto = offset
if whence == os.SEEK_SET:
seekto += self._start
elif whence == os.SEEK_CUR:
seekto += self._start + self._pos
elif whence == os.SEEK_END:
seekto += self._end
else:
raise IOError("Invalid whence argument to seek: %r" % (whence,))
self._checkwindow(seekto, 'seek')
self._fileobj.seek(seekto)
self._pos = seekto - self._start
def write(self, bytes):
here = self._start + self._pos
self._checkwindow(here, 'write')
self._checkwindow(here + len(bytes), 'write')
self._fileobj.seek(here, os.SEEK_SET)
self._fileobj.write(bytes)
self._pos += len(bytes)
def read(self, size=maxint):
assert size >= 0
here = self._start + self._pos
self._checkwindow(here, 'read')
size = min(size, self._end - here)
self._fileobj.seek(here, os.SEEK_SET)
bytes = self._fileobj.read(size)
self._pos += len(bytes)
return bytes
def read_data(file, endian, num=1):
"""
Read a given number of 32-bits unsigned integers from the given file
with the given endianness.
"""
res = struct.unpack(endian + 'L' * num, file.read(num * 4))
if len(res) == 1:
return res[0]
return res
def mach_o_change(path, what, value):
"""
Replace a given name (what) in any LC_LOAD_DYLIB command found in
the given binary with a new name (value), provided it's shorter.
"""
def do_macho(file, bits, endian):
# Read Mach-O header (the magic number is assumed read by the caller)
cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = read_data(file, endian, 6)
# 64-bits header has one more field.
if bits == 64:
read_data(file, endian)
# The header is followed by ncmds commands
for n in range(ncmds):
where = file.tell()
# Read command header
cmd, cmdsize = read_data(file, endian, 2)
if cmd == LC_LOAD_DYLIB:
# The first data field in LC_LOAD_DYLIB commands is the
# offset of the name, starting from the beginning of the
# command.
name_offset = read_data(file, endian)
file.seek(where + name_offset, os.SEEK_SET)
# Read the NUL terminated string
load = file.read(cmdsize - name_offset)
load = load[:load.index('\0')]
# If the string is what is being replaced, overwrite it.
if load == what:
file.seek(where + name_offset, os.SEEK_SET)
file.write(value + '\0')
# Seek to the next command
file.seek(where + cmdsize, os.SEEK_SET)
def do_file(file, offset=0, size=maxint):
file = fileview(file, offset, size)
# Read magic number
magic = read_data(file, BIG_ENDIAN)
if magic == FAT_MAGIC:
# Fat binaries contain nfat_arch Mach-O binaries
nfat_arch = read_data(file, BIG_ENDIAN)
for n in range(nfat_arch):
# Read arch header
cputype, cpusubtype, offset, size, align = read_data(file, BIG_ENDIAN, 5)
do_file(file, offset, size)
elif magic == MH_MAGIC:
do_macho(file, 32, BIG_ENDIAN)
elif magic == MH_CIGAM:
do_macho(file, 32, LITTLE_ENDIAN)
elif magic == MH_MAGIC_64:
do_macho(file, 64, BIG_ENDIAN)
elif magic == MH_CIGAM_64:
do_macho(file, 64, LITTLE_ENDIAN)
assert(len(what) >= len(value))
do_file(open(path, 'r+b'))
if __name__ == '__main__':
main()

View File

@ -2,7 +2,7 @@
# You cannot run it directly.
# Created by Davide Di Blasi <davidedb@gmail.com>.
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate'
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc'
# Unset irrelavent variables.
deactivate nondestructive
@ -28,5 +28,7 @@ endif
set prompt = "[$env_name] $prompt"
unset env_name
alias pydoc python -m pydoc
rehash

View File

@ -11,12 +11,17 @@ function deactivate -d "Exit virtualenv and return to normal shell environment"
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
set -e _OLD_VIRTUAL_PYTHONHOME
end
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
functions -e fish_prompt
set -e _OLD_FISH_PROMPT_OVERRIDE
. ( begin
printf "function fish_prompt\n\t#"
functions _old_fish_prompt
end | psub )
functions -e _old_fish_prompt
end
set -e VIRTUAL_ENV
if test "$argv[1]" != "nondestructive"
# Self destruct!
@ -39,41 +44,31 @@ if set -q PYTHONHOME
end
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
# fish shell uses a function, instead of env vars,
# to produce the prompt. Overriding the existing function is easy.
# However, adding to the current prompt, instead of clobbering it,
# is a little more work.
set -l oldpromptfile (tempfile)
if test $status
# save the current fish_prompt function...
echo "function _old_fish_prompt" >> $oldpromptfile
echo -n \# >> $oldpromptfile
functions fish_prompt >> $oldpromptfile
# we've made the "_old_fish_prompt" file, source it.
. $oldpromptfile
rm -f $oldpromptfile
# fish uses a function instead of an env var to generate the prompt.
# save the current fish_prompt function as the function _old_fish_prompt
. ( begin
printf "function _old_fish_prompt\n\t#"
functions fish_prompt
end | psub )
# with the original prompt function renamed, we can override with our own.
function fish_prompt
# Prompt override?
if test -n "__VIRTUAL_PROMPT__"
# We've been given us a prompt override.
#
# FIXME: Unsure how to handle this *safely*. We could just eval()
# whatever is given, but the risk is a bit much.
echo "activate.fish: Alternative prompt prefix is not supported under fish-shell." 1>&2
echo "activate.fish: Alter the fish_prompt in this file as needed." 1>&2
end
# with the original prompt function renamed, we can override with our own.
function fish_prompt
set -l _checkbase (basename "$VIRTUAL_ENV")
if test $_checkbase = "__"
# special case for Aspen magic directories
# see http://www.zetadev.com/software/aspen/
printf "%s[%s]%s %s" (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal) (_old_fish_prompt)
else
printf "%s(%s)%s%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal) (_old_fish_prompt)
end
end
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
end
printf "%s%s%s" "__VIRTUAL_PROMPT__" (set_color normal) (_old_fish_prompt)
return
end
# ...Otherwise, prepend env
set -l _checkbase (basename "$VIRTUAL_ENV")
if test $_checkbase = "__"
# special case for Aspen magic directories
# see http://www.zetadev.com/software/aspen/
printf "%s[%s]%s %s" (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal) (_old_fish_prompt)
else
printf "%s(%s)%s%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal) (_old_fish_prompt)
end
end
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
end

View File

@ -2,6 +2,8 @@
# you cannot run it directly
deactivate () {
unset pydoc
# reset old environment variables
if [ -n "$_OLD_VIRTUAL_PATH" ] ; then
PATH="$_OLD_VIRTUAL_PATH"
@ -18,7 +20,7 @@ deactivate () {
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then
hash -r
hash -r 2>/dev/null
fi
if [ -n "$_OLD_VIRTUAL_PS1" ] ; then
@ -68,9 +70,11 @@ if [ -z "$VIRTUAL_ENV_DISABLE_PROMPT" ] ; then
export PS1
fi
alias pydoc="python -m pydoc"
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then
hash -r
hash -r 2>/dev/null
fi

View File

@ -14,6 +14,7 @@ the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import os
import shutil
import sys
import time
import fnmatch
@ -46,7 +47,7 @@ except ImportError:
args = [quote(arg) for arg in args]
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
DEFAULT_VERSION = "0.6.24"
DEFAULT_VERSION = "0.6.27"
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
SETUPTOOLS_FAKED_VERSION = "0.6c11"
@ -63,7 +64,7 @@ Description: xxx
""" % SETUPTOOLS_FAKED_VERSION
def _install(tarball):
def _install(tarball, install_args=()):
# extracting the tarball
tmpdir = tempfile.mkdtemp()
log.warn('Extracting in %s', tmpdir)
@ -81,11 +82,12 @@ def _install(tarball):
# installing
log.warn('Installing Distribute')
if not _python_cmd('setup.py', 'install'):
if not _python_cmd('setup.py', 'install', *install_args):
log.warn('Something went wrong during the installation.')
log.warn('See the error message above.')
finally:
os.chdir(old_wd)
shutil.rmtree(tmpdir)
def _build_egg(egg, tarball, to_dir):
@ -110,6 +112,7 @@ def _build_egg(egg, tarball, to_dir):
finally:
os.chdir(old_wd)
shutil.rmtree(tmpdir)
# returning the result
log.warn(egg)
if not os.path.exists(egg):
@ -306,6 +309,9 @@ def _create_fake_setuptools_pkg_info(placeholder):
log.warn('%s already exists', pkg_info)
return
if not os.access(pkg_info, os.W_OK):
log.warn("Don't have permissions to write %s, skipping", pkg_info)
log.warn('Creating %s', pkg_info)
f = open(pkg_info, 'w')
try:
@ -474,11 +480,20 @@ def _extractall(self, path=".", members=None):
else:
self._dbg(1, "tarfile: %s" % e)
def _build_install_args(argv):
install_args = []
user_install = '--user' in argv
if user_install and sys.version_info < (2,6):
log.warn("--user requires Python 2.6 or later")
raise SystemExit(1)
if user_install:
install_args.append('--user')
return install_args
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
tarball = download_setuptools()
_install(tarball)
_install(tarball, _build_install_args(argv))
if __name__ == '__main__':

View File

@ -15,11 +15,8 @@ else:
__path__.insert(0, distutils_path)
exec(open(os.path.join(distutils_path, '__init__.py')).read())
try:
import dist
import sysconfig
except ImportError:
from distutils import dist, sysconfig
from distutils import dist, sysconfig
try:
basestring
except NameError:

View File

@ -422,7 +422,7 @@ class _Printer(object):
for filename in self.__files:
filename = os.path.join(dir, filename)
try:
fp = file(filename, "rU")
fp = open(filename, "rU")
data = fp.read()
fp.close()
break
@ -553,9 +553,7 @@ def virtual_install_main_packages():
hardcoded_relative_dirs = []
if sys.path[0] == '':
pos += 1
if sys.platform == 'win32':
paths = [os.path.join(sys.real_prefix, 'Lib'), os.path.join(sys.real_prefix, 'DLLs')]
elif _is_jython:
if _is_jython:
paths = [os.path.join(sys.real_prefix, 'Lib')]
elif _is_pypy:
if sys.pypy_version_info >= (1, 5):
@ -572,6 +570,8 @@ def virtual_install_main_packages():
plat_path = os.path.join(path, 'plat-%s' % sys.platform)
if os.path.exists(plat_path):
paths.append(plat_path)
elif sys.platform == 'win32':
paths = [os.path.join(sys.real_prefix, 'Lib'), os.path.join(sys.real_prefix, 'DLLs')]
else:
paths = [os.path.join(sys.real_prefix, 'lib', 'python'+sys.version[:3])]
hardcoded_relative_dirs = paths[:] # for the special 'darwin' case below

View File

@ -89,6 +89,10 @@ DEFINES += \
-DDLL_SUFFIX=\"$(DLL_SUFFIX)\" \
$(NULL)
EXPORTS += \
nsNSSShutDown.h \
$(NULL)
# Use local includes because they are inserted before INCLUDES
# so that Mozilla's nss.h is used, not glibc's
LOCAL_INCLUDES += $(NSS_CFLAGS)

View File

@ -1 +1 @@
http://hg.mozilla.org/projects/addon-sdk/archive/041eaf6ae5fb.tar.bz2
http://hg.mozilla.org/projects/addon-sdk/archive/8d6150ab4903.tar.bz2

View File

@ -5406,61 +5406,37 @@ nsWindow::ClientMarginHitTestPoint(PRInt32 mx, PRInt32 my)
if (mSizeMode == nsSizeMode_Maximized)
isResizable = false;
// Ensure being accessible to borders of window. Even if contents are in
// this area, the area must behave as border.
nsIntMargin nonClientSize(NS_MAX(mHorResizeMargin - mNonClientOffset.left,
kResizableBorderMinSize),
NS_MAX(mCaptionHeight - mNonClientOffset.top,
kResizableBorderMinSize),
NS_MAX(mHorResizeMargin - mNonClientOffset.right,
kResizableBorderMinSize),
NS_MAX(mVertResizeMargin - mNonClientOffset.bottom,
kResizableBorderMinSize));
bool allowContentOverride = mSizeMode == nsSizeMode_Maximized ||
(mx >= winRect.left + nonClientSize.left &&
mx <= winRect.right - nonClientSize.right &&
my >= winRect.top + nonClientSize.top &&
my <= winRect.bottom - nonClientSize.bottom);
// The border size. If there is no content under mouse cursor, the border
// size should be larger than the values in system settings. Otherwise,
// contents under the mouse cursor should be able to override the behavior.
// E.g., user must expect that Firefox button always opens the popup menu
// even when the user clicks on the above edge of it.
nsIntMargin borderSize(NS_MAX(nonClientSize.left, mHorResizeMargin),
NS_MAX(nonClientSize.top, mVertResizeMargin),
NS_MAX(nonClientSize.right, mHorResizeMargin),
NS_MAX(nonClientSize.bottom, mVertResizeMargin));
bool contentMayOverlap = false;
bool top = false;
bool bottom = false;
bool left = false;
bool right = false;
if (my >= winRect.top && my < winRect.top + borderSize.top) {
top = true;
contentMayOverlap = (my >= winRect.top + nonClientSize.top);
} else if (my <= winRect.bottom && my > winRect.bottom - borderSize.bottom) {
bottom = true;
contentMayOverlap = (my <= winRect.bottom - nonClientSize.bottom);
}
int topOffset = NS_MAX(mCaptionHeight - mNonClientOffset.top,
kResizableBorderMinSize);
int bottomOffset = NS_MAX(mVertResizeMargin - mNonClientOffset.bottom,
kResizableBorderMinSize);
int topBounds = winRect.top + topOffset;
int bottomBounds = winRect.bottom - bottomOffset;
if (my >= winRect.top && my < topBounds)
top = true;
else if (my <= winRect.bottom && my > bottomBounds)
bottom = true;
int leftOffset = NS_MAX(mHorResizeMargin - mNonClientOffset.left,
kResizableBorderMinSize);
int rightOffset = NS_MAX(mHorResizeMargin - mNonClientOffset.right,
kResizableBorderMinSize);
// (the 2x case here doubles the resize area for corners)
int multiplier = (top || bottom) ? 2 : 1;
if (mx >= winRect.left &&
mx < winRect.left + (multiplier * borderSize.left)) {
int leftBounds = winRect.left +
(bottom ? (2*leftOffset) : leftOffset);
int rightBounds = winRect.right -
(bottom ? (2*rightOffset) : rightOffset);
if (mx >= winRect.left && mx < leftBounds)
left = true;
contentMayOverlap =
contentMayOverlap || (mx >= winRect.left + nonClientSize.left);
} else if (mx <= winRect.right &&
mx > winRect.right - (multiplier * borderSize.right)) {
else if (mx <= winRect.right && mx > rightBounds)
right = true;
contentMayOverlap =
contentMayOverlap || (mx <= winRect.right - nonClientSize.right);
}
if (isResizable) {
if (top) {
@ -5488,7 +5464,22 @@ nsWindow::ClientMarginHitTestPoint(PRInt32 mx, PRInt32 my)
testResult = HTBORDER;
}
if (!sIsInMouseCapture && allowContentOverride && contentMayOverlap) {
bool contentOverlap = true;
if (mSizeMode != nsSizeMode_Maximized) {
contentOverlap = mx >= winRect.left + leftOffset &&
mx <= winRect.right - rightOffset &&
my >= winRect.top + topOffset &&
my <= winRect.bottom - bottomOffset;
}
if (!sIsInMouseCapture &&
contentOverlap &&
(testResult == HTCLIENT ||
testResult == HTTOP ||
testResult == HTBORDER ||
testResult == HTTOPLEFT ||
testResult == HTCAPTION)) {
LPARAM lParam = MAKELPARAM(mx, my);
LPARAM lParamClient = lParamToClient(lParam);
bool result = DispatchMouseEvent(NS_MOUSE_MOZHITTEST, 0, lParamClient,