mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-01 12:57:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
34
netwerk/protocol/about/moz.build
Normal file
34
netwerk/protocol/about/moz.build
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIAboutModule.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'necko_about'
|
||||
|
||||
EXPORTS += [
|
||||
'nsAboutProtocolUtils.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'nsAboutBlank.cpp',
|
||||
'nsAboutCache.cpp',
|
||||
'nsAboutCacheEntry.cpp',
|
||||
'nsAboutProtocolHandler.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/netwerk/base',
|
||||
'/netwerk/cache2',
|
||||
]
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
59
netwerk/protocol/about/nsAboutBlank.cpp
Normal file
59
netwerk/protocol/about/nsAboutBlank.cpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsAboutBlank.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsAboutBlank, nsIAboutModule)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutBlank::NewChannel(nsIURI* aURI,
|
||||
nsILoadInfo* aLoadInfo,
|
||||
nsIChannel** result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aURI);
|
||||
|
||||
nsCOMPtr<nsIInputStream> in;
|
||||
nsresult rv = NS_NewCStringInputStream(getter_AddRefs(in), EmptyCString());
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel),
|
||||
aURI,
|
||||
in,
|
||||
NS_LITERAL_CSTRING("text/html"),
|
||||
NS_LITERAL_CSTRING("utf-8"),
|
||||
aLoadInfo);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
channel.forget(result);
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutBlank::GetURIFlags(nsIURI *aURI, uint32_t *result)
|
||||
{
|
||||
*result = nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
|
||||
nsIAboutModule::MAKE_LINKABLE |
|
||||
nsIAboutModule::HIDE_FROM_ABOUTABOUT;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAboutBlank::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
|
||||
{
|
||||
nsAboutBlank* about = new nsAboutBlank();
|
||||
if (about == nullptr)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(about);
|
||||
nsresult rv = about->QueryInterface(aIID, aResult);
|
||||
NS_RELEASE(about);
|
||||
return rv;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
35
netwerk/protocol/about/nsAboutBlank.h
Normal file
35
netwerk/protocol/about/nsAboutBlank.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 nsAboutBlank_h__
|
||||
#define nsAboutBlank_h__
|
||||
|
||||
#include "nsIAboutModule.h"
|
||||
|
||||
class nsAboutBlank : public nsIAboutModule
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_DECL_NSIABOUTMODULE
|
||||
|
||||
nsAboutBlank() {}
|
||||
|
||||
static nsresult
|
||||
Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
|
||||
|
||||
private:
|
||||
virtual ~nsAboutBlank() {}
|
||||
};
|
||||
|
||||
#define NS_ABOUT_BLANK_MODULE_CID \
|
||||
{ /* 3decd6c8-30ef-11d3-8cd0-0060b0fc14a3 */ \
|
||||
0x3decd6c8, \
|
||||
0x30ef, \
|
||||
0x11d3, \
|
||||
{0x8c, 0xd0, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \
|
||||
}
|
||||
|
||||
#endif // nsAboutBlank_h__
|
||||
585
netwerk/protocol/about/nsAboutCache.cpp
Normal file
585
netwerk/protocol/about/nsAboutCache.cpp
Normal file
|
|
@ -0,0 +1,585 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsAboutCache.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIStorageStream.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIPipe.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsEscape.h"
|
||||
#include "nsAboutProtocolUtils.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
#include "nsICacheStorageService.h"
|
||||
#include "nsICacheStorage.h"
|
||||
#include "CacheFileUtils.h"
|
||||
#include "CacheObserver.h"
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
using namespace mozilla::net;
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsAboutCache, nsIAboutModule)
|
||||
NS_IMPL_ISUPPORTS(nsAboutCache::Channel, nsIChannel, nsIRequest, nsICacheStorageVisitor)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutCache::NewChannel(nsIURI* aURI,
|
||||
nsILoadInfo* aLoadInfo,
|
||||
nsIChannel** result)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aURI);
|
||||
|
||||
RefPtr<Channel> channel = new Channel();
|
||||
rv = channel->Init(aURI, aLoadInfo);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
channel.forget(result);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAboutCache::Channel::Init(nsIURI* aURI, nsILoadInfo* aLoadInfo)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
mCancel = false;
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
rv = NS_NewPipe(getter_AddRefs(inputStream), getter_AddRefs(mStream),
|
||||
16384, (uint32_t)-1,
|
||||
true, // non-blocking input
|
||||
false // blocking output
|
||||
);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsAutoCString storageName;
|
||||
rv = ParseURI(aURI, storageName);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
mOverview = storageName.IsEmpty();
|
||||
if (mOverview) {
|
||||
// ...and visit all we can
|
||||
mStorageList.AppendElement(NS_LITERAL_CSTRING("memory"));
|
||||
mStorageList.AppendElement(NS_LITERAL_CSTRING("disk"));
|
||||
mStorageList.AppendElement(NS_LITERAL_CSTRING("appcache"));
|
||||
} else {
|
||||
// ...and visit just the specified storage, entries will output too
|
||||
mStorageList.AppendElement(storageName);
|
||||
}
|
||||
|
||||
// The entries header is added on encounter of the first entry
|
||||
mEntriesHeaderAdded = false;
|
||||
|
||||
rv = NS_NewInputStreamChannelInternal(getter_AddRefs(mChannel),
|
||||
aURI,
|
||||
inputStream,
|
||||
NS_LITERAL_CSTRING("text/html"),
|
||||
NS_LITERAL_CSTRING("utf-8"),
|
||||
aLoadInfo);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
mBuffer.AssignLiteral(
|
||||
"<!DOCTYPE html>\n"
|
||||
"<html>\n"
|
||||
"<head>\n"
|
||||
" <title>Network Cache Storage Information</title>\n"
|
||||
" <meta charset=\"utf-8\">\n"
|
||||
" <link rel=\"stylesheet\" href=\"chrome://global/skin/about.css\"/>\n"
|
||||
" <link rel=\"stylesheet\" href=\"chrome://global/skin/aboutCache.css\"/>\n"
|
||||
" <script src=\"chrome://global/content/aboutCache.js\"></script>"
|
||||
"</head>\n"
|
||||
"<body class=\"aboutPageWideContainer\">\n"
|
||||
"<h1>Information about the Network Cache Storage Service</h1>\n");
|
||||
|
||||
// Add the context switch controls
|
||||
mBuffer.AppendLiteral(
|
||||
"<label><input id='priv' type='checkbox'/> Private</label>\n"
|
||||
"<label><input id='anon' type='checkbox'/> Anonymous</label>\n"
|
||||
);
|
||||
|
||||
if (CacheObserver::UseNewCache()) {
|
||||
// Visit scoping by browser and appid is not implemented for
|
||||
// the old cache, simply don't add these controls.
|
||||
// The appid/inbrowser entries are already mixed in the default
|
||||
// view anyway.
|
||||
mBuffer.AppendLiteral(
|
||||
"<label><input id='appid' type='text' size='6'/> AppID</label>\n"
|
||||
"<label><input id='inbrowser' type='checkbox'/> In Browser Element</label>\n"
|
||||
);
|
||||
}
|
||||
|
||||
mBuffer.AppendLiteral(
|
||||
"<label><input id='submit' type='button' value='Update' onclick='navigate()'/></label>\n"
|
||||
);
|
||||
|
||||
if (!mOverview) {
|
||||
mBuffer.AppendLiteral("<a href=\"about:cache?storage=&context=");
|
||||
char* escapedContext = nsEscapeHTML(mContextString.get());
|
||||
mBuffer.Append(escapedContext);
|
||||
free(escapedContext);
|
||||
mBuffer.AppendLiteral("\">Back to overview</a>");
|
||||
}
|
||||
|
||||
rv = FlushBuffer();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to flush buffer");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAboutCache::Channel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
if (!mChannel) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Kick the walk loop.
|
||||
rv = VisitNextStorage();
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
MOZ_ASSERT(!aContext, "asyncOpen2() does not take a context argument");
|
||||
rv = NS_MaybeOpenChannelUsingAsyncOpen2(mChannel, aListener);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAboutCache::Channel::AsyncOpen2(nsIStreamListener *aListener)
|
||||
{
|
||||
return AsyncOpen(aListener, nullptr);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAboutCache::Channel::Open(nsIInputStream * *_retval)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsAboutCache::Channel::Open2(nsIInputStream * *_retval)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAboutCache::Channel::ParseURI(nsIURI * uri, nsACString & storage)
|
||||
{
|
||||
//
|
||||
// about:cache[?storage=<storage-name>[&context=<context-key>]]
|
||||
//
|
||||
nsresult rv;
|
||||
|
||||
nsAutoCString path;
|
||||
rv = uri->GetPath(path);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
mContextString.Truncate();
|
||||
mLoadInfo = CacheFileUtils::ParseKey(NS_LITERAL_CSTRING(""));
|
||||
storage.Truncate();
|
||||
|
||||
nsACString::const_iterator start, valueStart, end;
|
||||
path.BeginReading(start);
|
||||
path.EndReading(end);
|
||||
|
||||
valueStart = end;
|
||||
if (!FindInReadable(NS_LITERAL_CSTRING("?storage="), start, valueStart)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsACString::const_iterator storageNameBegin = valueStart;
|
||||
|
||||
start = valueStart;
|
||||
valueStart = end;
|
||||
if (!FindInReadable(NS_LITERAL_CSTRING("&context="), start, valueStart))
|
||||
start = end;
|
||||
|
||||
nsACString::const_iterator storageNameEnd = start;
|
||||
|
||||
mContextString = Substring(valueStart, end);
|
||||
mLoadInfo = CacheFileUtils::ParseKey(mContextString);
|
||||
storage.Assign(Substring(storageNameBegin, storageNameEnd));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAboutCache::Channel::VisitNextStorage()
|
||||
{
|
||||
if (!mStorageList.Length())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
mStorageName = mStorageList[0];
|
||||
mStorageList.RemoveElementAt(0);
|
||||
|
||||
// Must re-dispatch since we cannot start another visit cycle
|
||||
// from visitor callback. The cache v1 service doesn't like it.
|
||||
// TODO - mayhemer, bug 913828, remove this dispatch and call
|
||||
// directly.
|
||||
return NS_DispatchToMainThread(mozilla::NewRunnableMethod(this, &nsAboutCache::Channel::FireVisitStorage));
|
||||
}
|
||||
|
||||
void
|
||||
nsAboutCache::Channel::FireVisitStorage()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
rv = VisitStorage(mStorageName);
|
||||
if (NS_FAILED(rv)) {
|
||||
if (mLoadInfo) {
|
||||
char* escaped = nsEscapeHTML(mStorageName.get());
|
||||
mBuffer.Append(
|
||||
nsPrintfCString("<p>Unrecognized storage name '%s' in about:cache URL</p>",
|
||||
escaped));
|
||||
free(escaped);
|
||||
} else {
|
||||
char* escaped = nsEscapeHTML(mContextString.get());
|
||||
mBuffer.Append(
|
||||
nsPrintfCString("<p>Unrecognized context key '%s' in about:cache URL</p>",
|
||||
escaped));
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
rv = FlushBuffer();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to flush buffer");
|
||||
}
|
||||
|
||||
// Simulate finish of a visit cycle, this tries the next storage
|
||||
// or closes the output stream (i.e. the UI loader will stop spinning)
|
||||
OnCacheEntryVisitCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAboutCache::Channel::VisitStorage(nsACString const & storageName)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
rv = GetStorage(storageName, mLoadInfo, getter_AddRefs(mStorage));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = mStorage->AsyncVisitStorage(this, !mOverview);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//static
|
||||
nsresult
|
||||
nsAboutCache::GetStorage(nsACString const & storageName,
|
||||
nsILoadContextInfo* loadInfo,
|
||||
nsICacheStorage **storage)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsICacheStorageService> cacheService =
|
||||
do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsICacheStorage> cacheStorage;
|
||||
if (storageName == "disk") {
|
||||
rv = cacheService->DiskCacheStorage(
|
||||
loadInfo, false, getter_AddRefs(cacheStorage));
|
||||
} else if (storageName == "memory") {
|
||||
rv = cacheService->MemoryCacheStorage(
|
||||
loadInfo, getter_AddRefs(cacheStorage));
|
||||
} else if (storageName == "appcache") {
|
||||
rv = cacheService->AppCacheStorage(
|
||||
loadInfo, nullptr, getter_AddRefs(cacheStorage));
|
||||
} else {
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
cacheStorage.forget(storage);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutCache::Channel::OnCacheStorageInfo(uint32_t aEntryCount, uint64_t aConsumption,
|
||||
uint64_t aCapacity, nsIFile * aDirectory)
|
||||
{
|
||||
// We need mStream for this
|
||||
if (!mStream) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mBuffer.AssignLiteral("<h2>");
|
||||
mBuffer.Append(mStorageName);
|
||||
mBuffer.AppendLiteral("</h2>\n"
|
||||
"<table id=\"");
|
||||
mBuffer.AppendLiteral("\">\n");
|
||||
|
||||
// Write out cache info
|
||||
// Number of entries
|
||||
mBuffer.AppendLiteral(" <tr>\n"
|
||||
" <th>Number of entries:</th>\n"
|
||||
" <td>");
|
||||
mBuffer.AppendInt(aEntryCount);
|
||||
mBuffer.AppendLiteral("</td>\n"
|
||||
" </tr>\n");
|
||||
|
||||
// Maximum storage size
|
||||
mBuffer.AppendLiteral(" <tr>\n"
|
||||
" <th>Maximum storage size:</th>\n"
|
||||
" <td>");
|
||||
mBuffer.AppendInt(aCapacity / 1024);
|
||||
mBuffer.AppendLiteral(" KiB</td>\n"
|
||||
" </tr>\n");
|
||||
|
||||
// Storage in use
|
||||
mBuffer.AppendLiteral(" <tr>\n"
|
||||
" <th>Storage in use:</th>\n"
|
||||
" <td>");
|
||||
mBuffer.AppendInt(aConsumption / 1024);
|
||||
mBuffer.AppendLiteral(" KiB</td>\n"
|
||||
" </tr>\n");
|
||||
|
||||
// Storage disk location
|
||||
mBuffer.AppendLiteral(" <tr>\n"
|
||||
" <th>Storage disk location:</th>\n"
|
||||
" <td>");
|
||||
if (aDirectory) {
|
||||
nsAutoString path;
|
||||
aDirectory->GetPath(path);
|
||||
mBuffer.Append(NS_ConvertUTF16toUTF8(path));
|
||||
} else {
|
||||
mBuffer.AppendLiteral("none, only stored in memory");
|
||||
}
|
||||
mBuffer.AppendLiteral(" </td>\n"
|
||||
" </tr>\n");
|
||||
|
||||
if (mOverview) { // The about:cache case
|
||||
if (aEntryCount != 0) { // Add the "List Cache Entries" link
|
||||
mBuffer.AppendLiteral(" <tr>\n"
|
||||
" <th><a href=\"about:cache?storage=");
|
||||
mBuffer.Append(mStorageName);
|
||||
mBuffer.AppendLiteral("&context=");
|
||||
char* escapedContext = nsEscapeHTML(mContextString.get());
|
||||
mBuffer.Append(escapedContext);
|
||||
free(escapedContext);
|
||||
mBuffer.AppendLiteral("\">List Cache Entries</a></th>\n"
|
||||
" </tr>\n");
|
||||
}
|
||||
}
|
||||
|
||||
mBuffer.AppendLiteral("</table>\n");
|
||||
|
||||
// The entries header is added on encounter of the first entry
|
||||
mEntriesHeaderAdded = false;
|
||||
|
||||
nsresult rv = FlushBuffer();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to flush buffer");
|
||||
}
|
||||
|
||||
if (mOverview) {
|
||||
// OnCacheEntryVisitCompleted() is not called when we do not iterate
|
||||
// cache entries. Since this moves forward to the next storage in
|
||||
// the list we want to visit, artificially call it here.
|
||||
OnCacheEntryVisitCompleted();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutCache::Channel::OnCacheEntryInfo(nsIURI *aURI, const nsACString & aIdEnhance,
|
||||
int64_t aDataSize, int32_t aFetchCount,
|
||||
uint32_t aLastModified, uint32_t aExpirationTime,
|
||||
bool aPinned)
|
||||
{
|
||||
// We need mStream for this
|
||||
if (!mStream || mCancel) {
|
||||
// Returning a failure from this callback stops the iteration
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mEntriesHeaderAdded) {
|
||||
mBuffer.AppendLiteral("<hr/>\n"
|
||||
"<table id=\"entries\">\n"
|
||||
" <colgroup>\n"
|
||||
" <col id=\"col-key\">\n"
|
||||
" <col id=\"col-dataSize\">\n"
|
||||
" <col id=\"col-fetchCount\">\n"
|
||||
" <col id=\"col-lastModified\">\n"
|
||||
" <col id=\"col-expires\">\n"
|
||||
" <col id=\"col-pinned\">\n"
|
||||
" </colgroup>\n"
|
||||
" <thead>\n"
|
||||
" <tr>\n"
|
||||
" <th>Key</th>\n"
|
||||
" <th>Data size</th>\n"
|
||||
" <th>Fetch count</th>\n"
|
||||
" <th>Last Modifed</th>\n"
|
||||
" <th>Expires</th>\n"
|
||||
" <th>Pinning</th>\n"
|
||||
" </tr>\n"
|
||||
" </thead>\n");
|
||||
mEntriesHeaderAdded = true;
|
||||
}
|
||||
|
||||
// Generate a about:cache-entry URL for this entry...
|
||||
|
||||
nsAutoCString url;
|
||||
url.AssignLiteral("about:cache-entry?storage=");
|
||||
url.Append(mStorageName);
|
||||
|
||||
url.AppendLiteral("&context=");
|
||||
char* escapedContext = nsEscapeHTML(mContextString.get());
|
||||
url += escapedContext;
|
||||
free(escapedContext);
|
||||
|
||||
url.AppendLiteral("&eid=");
|
||||
char* escapedEID = nsEscapeHTML(aIdEnhance.BeginReading());
|
||||
url += escapedEID;
|
||||
free(escapedEID);
|
||||
|
||||
nsAutoCString cacheUriSpec;
|
||||
aURI->GetAsciiSpec(cacheUriSpec);
|
||||
char* escapedCacheURI = nsEscapeHTML(cacheUriSpec.get());
|
||||
url.AppendLiteral("&uri=");
|
||||
url += escapedCacheURI;
|
||||
|
||||
// Entry start...
|
||||
mBuffer.AppendLiteral(" <tr>\n");
|
||||
|
||||
// URI
|
||||
mBuffer.AppendLiteral(" <td><a href=\"");
|
||||
mBuffer.Append(url);
|
||||
mBuffer.AppendLiteral("\">");
|
||||
if (!aIdEnhance.IsEmpty()) {
|
||||
mBuffer.Append(aIdEnhance);
|
||||
mBuffer.Append(':');
|
||||
}
|
||||
mBuffer.Append(escapedCacheURI);
|
||||
mBuffer.AppendLiteral("</a></td>\n");
|
||||
|
||||
free(escapedCacheURI);
|
||||
|
||||
// Content length
|
||||
mBuffer.AppendLiteral(" <td>");
|
||||
mBuffer.AppendInt(aDataSize);
|
||||
mBuffer.AppendLiteral(" bytes</td>\n");
|
||||
|
||||
// Number of accesses
|
||||
mBuffer.AppendLiteral(" <td>");
|
||||
mBuffer.AppendInt(aFetchCount);
|
||||
mBuffer.AppendLiteral("</td>\n");
|
||||
|
||||
// vars for reporting time
|
||||
char buf[255];
|
||||
|
||||
// Last modified time
|
||||
mBuffer.AppendLiteral(" <td>");
|
||||
if (aLastModified) {
|
||||
PrintTimeString(buf, sizeof(buf), aLastModified);
|
||||
mBuffer.Append(buf);
|
||||
} else {
|
||||
mBuffer.AppendLiteral("No last modified time");
|
||||
}
|
||||
mBuffer.AppendLiteral("</td>\n");
|
||||
|
||||
// Expires time
|
||||
mBuffer.AppendLiteral(" <td>");
|
||||
if (aExpirationTime < 0xFFFFFFFF) {
|
||||
PrintTimeString(buf, sizeof(buf), aExpirationTime);
|
||||
mBuffer.Append(buf);
|
||||
} else {
|
||||
mBuffer.AppendLiteral("No expiration time");
|
||||
}
|
||||
mBuffer.AppendLiteral("</td>\n");
|
||||
|
||||
// Pinning
|
||||
mBuffer.AppendLiteral(" <td>");
|
||||
if (aPinned) {
|
||||
mBuffer.Append(NS_LITERAL_CSTRING("Pinned"));
|
||||
} else {
|
||||
mBuffer.Append(NS_LITERAL_CSTRING(" "));
|
||||
}
|
||||
mBuffer.AppendLiteral("</td>\n");
|
||||
|
||||
// Entry is done...
|
||||
mBuffer.AppendLiteral(" </tr>\n");
|
||||
|
||||
return FlushBuffer();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutCache::Channel::OnCacheEntryVisitCompleted()
|
||||
{
|
||||
if (!mStream) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (mEntriesHeaderAdded) {
|
||||
mBuffer.AppendLiteral("</table>\n");
|
||||
}
|
||||
|
||||
// Kick another storage visiting (from a storage that allows us.)
|
||||
while (mStorageList.Length()) {
|
||||
nsresult rv = VisitNextStorage();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Expecting new round of OnCache* calls.
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// We are done!
|
||||
mBuffer.AppendLiteral("</body>\n"
|
||||
"</html>\n");
|
||||
nsresult rv = FlushBuffer();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to flush buffer");
|
||||
}
|
||||
mStream->Close();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAboutCache::Channel::FlushBuffer()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
uint32_t bytesWritten;
|
||||
rv = mStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten);
|
||||
mBuffer.Truncate();
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
mCancel = true;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutCache::GetURIFlags(nsIURI *aURI, uint32_t *result)
|
||||
{
|
||||
*result = nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsAboutCache::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
|
||||
{
|
||||
nsAboutCache* about = new nsAboutCache();
|
||||
if (about == nullptr)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(about);
|
||||
nsresult rv = about->QueryInterface(aIID, aResult);
|
||||
NS_RELEASE(about);
|
||||
return rv;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
136
netwerk/protocol/about/nsAboutCache.h
Normal file
136
netwerk/protocol/about/nsAboutCache.h
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 nsAboutCache_h__
|
||||
#define nsAboutCache_h__
|
||||
|
||||
#include "nsIAboutModule.h"
|
||||
#include "nsICacheStorageVisitor.h"
|
||||
#include "nsICacheStorage.h"
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsILoadContextInfo.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#define NS_FORWARD_SAFE_NSICHANNEL_SUBSET(_to) \
|
||||
NS_IMETHOD GetOriginalURI(nsIURI * *aOriginalURI) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetOriginalURI(aOriginalURI); } \
|
||||
NS_IMETHOD SetOriginalURI(nsIURI *aOriginalURI) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetOriginalURI(aOriginalURI); } \
|
||||
NS_IMETHOD GetURI(nsIURI * *aURI) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetURI(aURI); } \
|
||||
NS_IMETHOD GetOwner(nsISupports * *aOwner) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetOwner(aOwner); } \
|
||||
NS_IMETHOD SetOwner(nsISupports *aOwner) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetOwner(aOwner); } \
|
||||
NS_IMETHOD GetNotificationCallbacks(nsIInterfaceRequestor * *aNotificationCallbacks) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetNotificationCallbacks(aNotificationCallbacks); } \
|
||||
NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor *aNotificationCallbacks) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetNotificationCallbacks(aNotificationCallbacks); } \
|
||||
NS_IMETHOD GetSecurityInfo(nsISupports * *aSecurityInfo) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetSecurityInfo(aSecurityInfo); } \
|
||||
NS_IMETHOD GetContentType(nsACString & aContentType) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentType(aContentType); } \
|
||||
NS_IMETHOD SetContentType(const nsACString & aContentType) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentType(aContentType); } \
|
||||
NS_IMETHOD GetContentCharset(nsACString & aContentCharset) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentCharset(aContentCharset); } \
|
||||
NS_IMETHOD SetContentCharset(const nsACString & aContentCharset) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentCharset(aContentCharset); } \
|
||||
NS_IMETHOD GetContentLength(int64_t *aContentLength) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentLength(aContentLength); } \
|
||||
NS_IMETHOD SetContentLength(int64_t aContentLength) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentLength(aContentLength); } \
|
||||
NS_IMETHOD GetContentDisposition(uint32_t *aContentDisposition) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentDisposition(aContentDisposition); } \
|
||||
NS_IMETHOD SetContentDisposition(uint32_t aContentDisposition) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentDisposition(aContentDisposition); } \
|
||||
NS_IMETHOD GetContentDispositionFilename(nsAString & aContentDispositionFilename) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentDispositionFilename(aContentDispositionFilename); } \
|
||||
NS_IMETHOD SetContentDispositionFilename(const nsAString & aContentDispositionFilename) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentDispositionFilename(aContentDispositionFilename); } \
|
||||
NS_IMETHOD GetContentDispositionHeader(nsACString & aContentDispositionHeader) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentDispositionHeader(aContentDispositionHeader); } \
|
||||
NS_IMETHOD GetLoadInfo(nsILoadInfo * *aLoadInfo) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetLoadInfo(aLoadInfo); } \
|
||||
NS_IMETHOD SetLoadInfo(nsILoadInfo *aLoadInfo) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetLoadInfo(aLoadInfo); } \
|
||||
|
||||
class nsAboutCache final : public nsIAboutModule
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIABOUTMODULE
|
||||
|
||||
nsAboutCache() {}
|
||||
|
||||
static MOZ_MUST_USE nsresult
|
||||
Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
|
||||
|
||||
static MOZ_MUST_USE nsresult
|
||||
GetStorage(nsACString const & storageName, nsILoadContextInfo* loadInfo,
|
||||
nsICacheStorage **storage);
|
||||
|
||||
protected:
|
||||
virtual ~nsAboutCache() {}
|
||||
|
||||
class Channel final : public nsIChannel
|
||||
, public nsICacheStorageVisitor
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICACHESTORAGEVISITOR
|
||||
NS_FORWARD_SAFE_NSIREQUEST(mChannel)
|
||||
NS_FORWARD_SAFE_NSICHANNEL_SUBSET(mChannel)
|
||||
NS_IMETHOD AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext) override;
|
||||
NS_IMETHOD AsyncOpen2(nsIStreamListener *aListener) override;
|
||||
NS_IMETHOD Open(nsIInputStream * *_retval) override;
|
||||
NS_IMETHOD Open2(nsIInputStream * *_retval) override;
|
||||
|
||||
private:
|
||||
virtual ~Channel() {}
|
||||
|
||||
public:
|
||||
MOZ_MUST_USE nsresult Init(nsIURI* aURI, nsILoadInfo* aLoadInfo);
|
||||
MOZ_MUST_USE nsresult ParseURI(nsIURI * uri, nsACString & storage);
|
||||
|
||||
// Finds a next storage we wish to visit (we use this method
|
||||
// even there is a specified storage name, which is the only
|
||||
// one in the list then.) Posts FireVisitStorage() when found.
|
||||
MOZ_MUST_USE nsresult VisitNextStorage();
|
||||
// Helper method that calls VisitStorage() for the current storage.
|
||||
// When it fails, OnCacheEntryVisitCompleted is simulated to close
|
||||
// the output stream and thus the about:cache channel.
|
||||
void FireVisitStorage();
|
||||
// Kiks the visit cycle for the given storage, names can be:
|
||||
// "disk", "memory", "appcache"
|
||||
// Note: any newly added storage type has to be manually handled here.
|
||||
MOZ_MUST_USE nsresult VisitStorage(nsACString const & storageName);
|
||||
|
||||
// Writes content of mBuffer to mStream and truncates
|
||||
// the buffer. It may fail when the input stream is closed by canceling
|
||||
// the input stream channel. It can be used to stop the cache iteration
|
||||
// process.
|
||||
MOZ_MUST_USE nsresult FlushBuffer();
|
||||
|
||||
// Whether we are showing overview status of all available
|
||||
// storages.
|
||||
bool mOverview;
|
||||
|
||||
// Flag initially false, that indicates the entries header has
|
||||
// been added to the output HTML.
|
||||
bool mEntriesHeaderAdded;
|
||||
|
||||
// Cancelation flag
|
||||
bool mCancel;
|
||||
|
||||
// The context we are working with.
|
||||
nsCOMPtr<nsILoadContextInfo> mLoadInfo;
|
||||
nsCString mContextString;
|
||||
|
||||
// The list of all storage names we want to visit
|
||||
nsTArray<nsCString> mStorageList;
|
||||
nsCString mStorageName;
|
||||
nsCOMPtr<nsICacheStorage> mStorage;
|
||||
|
||||
// Output data buffering and streaming output
|
||||
nsCString mBuffer;
|
||||
nsCOMPtr<nsIOutputStream> mStream;
|
||||
|
||||
// The input stream channel, the one that actually does the job
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
};
|
||||
};
|
||||
|
||||
#define NS_ABOUT_CACHE_MODULE_CID \
|
||||
{ /* 9158c470-86e4-11d4-9be2-00e09872a416 */ \
|
||||
0x9158c470, \
|
||||
0x86e4, \
|
||||
0x11d4, \
|
||||
{0x9b, 0xe2, 0x00, 0xe0, 0x98, 0x72, 0xa4, 0x16} \
|
||||
}
|
||||
|
||||
#endif // nsAboutCache_h__
|
||||
603
netwerk/protocol/about/nsAboutCacheEntry.cpp
Normal file
603
netwerk/protocol/about/nsAboutCacheEntry.cpp
Normal file
|
|
@ -0,0 +1,603 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsAboutCacheEntry.h"
|
||||
|
||||
#include "mozilla/Sprintf.h"
|
||||
|
||||
#include "nsAboutCache.h"
|
||||
#include "nsICacheStorage.h"
|
||||
#include "CacheObserver.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsEscape.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIAsyncOutputStream.h"
|
||||
#include "nsAboutProtocolUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsInputStreamPump.h"
|
||||
#include "CacheFileUtils.h"
|
||||
#include <algorithm>
|
||||
#include "nsIPipe.h"
|
||||
|
||||
using namespace mozilla::net;
|
||||
|
||||
#define HEXDUMP_MAX_ROWS 16
|
||||
|
||||
static void
|
||||
HexDump(uint32_t *state, const char *buf, int32_t n, nsCString &result)
|
||||
{
|
||||
char temp[16];
|
||||
|
||||
const unsigned char *p;
|
||||
while (n) {
|
||||
SprintfLiteral(temp, "%08x: ", *state);
|
||||
result.Append(temp);
|
||||
*state += HEXDUMP_MAX_ROWS;
|
||||
|
||||
p = (const unsigned char *) buf;
|
||||
|
||||
int32_t i, row_max = std::min(HEXDUMP_MAX_ROWS, n);
|
||||
|
||||
// print hex codes:
|
||||
for (i = 0; i < row_max; ++i) {
|
||||
SprintfLiteral(temp, "%02x ", *p++);
|
||||
result.Append(temp);
|
||||
}
|
||||
for (i = row_max; i < HEXDUMP_MAX_ROWS; ++i) {
|
||||
result.AppendLiteral(" ");
|
||||
}
|
||||
|
||||
// print ASCII glyphs if possible:
|
||||
p = (const unsigned char *) buf;
|
||||
for (i = 0; i < row_max; ++i, ++p) {
|
||||
switch (*p) {
|
||||
case '<':
|
||||
result.AppendLiteral("<");
|
||||
break;
|
||||
case '>':
|
||||
result.AppendLiteral(">");
|
||||
break;
|
||||
case '&':
|
||||
result.AppendLiteral("&");
|
||||
break;
|
||||
default:
|
||||
if (*p < 0x7F && *p > 0x1F) {
|
||||
result.Append(*p);
|
||||
} else {
|
||||
result.Append('.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.Append('\n');
|
||||
|
||||
buf += row_max;
|
||||
n -= row_max;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsAboutCacheEntry::nsISupports
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsAboutCacheEntry,
|
||||
nsIAboutModule)
|
||||
NS_IMPL_ISUPPORTS(nsAboutCacheEntry::Channel,
|
||||
nsICacheEntryOpenCallback,
|
||||
nsICacheEntryMetaDataVisitor,
|
||||
nsIStreamListener,
|
||||
nsIChannel)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsAboutCacheEntry::nsIAboutModule
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutCacheEntry::NewChannel(nsIURI* uri,
|
||||
nsILoadInfo* aLoadInfo,
|
||||
nsIChannel** result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(uri);
|
||||
nsresult rv;
|
||||
|
||||
RefPtr<Channel> channel = new Channel();
|
||||
rv = channel->Init(uri, aLoadInfo);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
channel.forget(result);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutCacheEntry::GetURIFlags(nsIURI *aURI, uint32_t *result)
|
||||
{
|
||||
*result = nsIAboutModule::HIDE_FROM_ABOUTABOUT |
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsAboutCacheEntry::Channel
|
||||
|
||||
nsresult
|
||||
nsAboutCacheEntry::Channel::Init(nsIURI* uri, nsILoadInfo* aLoadInfo)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
rv = GetContentStream(uri, getter_AddRefs(stream));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = NS_NewInputStreamChannelInternal(getter_AddRefs(mChannel),
|
||||
uri,
|
||||
stream,
|
||||
NS_LITERAL_CSTRING("text/html"),
|
||||
NS_LITERAL_CSTRING("utf-8"),
|
||||
aLoadInfo);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAboutCacheEntry::Channel::GetContentStream(nsIURI *uri, nsIInputStream **result)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Init: (block size, maximum length)
|
||||
nsCOMPtr<nsIAsyncInputStream> inputStream;
|
||||
rv = NS_NewPipe2(getter_AddRefs(inputStream),
|
||||
getter_AddRefs(mOutputStream),
|
||||
true, false,
|
||||
256, UINT32_MAX);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(
|
||||
buffer,
|
||||
"<!DOCTYPE html>\n"
|
||||
"<html>\n"
|
||||
"<head>\n"
|
||||
" <title>Cache entry information</title>\n"
|
||||
" <link rel=\"stylesheet\" "
|
||||
"href=\"chrome://global/skin/about.css\" type=\"text/css\"/>\n"
|
||||
" <link rel=\"stylesheet\" "
|
||||
"href=\"chrome://global/skin/aboutCacheEntry.css\" type=\"text/css\"/>\n"
|
||||
"</head>\n"
|
||||
"<body>\n"
|
||||
"<h1>Cache entry information</h1>\n");
|
||||
uint32_t n;
|
||||
rv = mOutputStream->Write(buffer.get(), buffer.Length(), &n);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (n != buffer.Length()) return NS_ERROR_UNEXPECTED;
|
||||
|
||||
rv = OpenCacheEntry(uri);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
inputStream.forget(result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAboutCacheEntry::Channel::OpenCacheEntry(nsIURI *uri)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
rv = ParseURI(uri, mStorageName, getter_AddRefs(mLoadInfo),
|
||||
mEnhanceId, getter_AddRefs(mCacheURI));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (!CacheObserver::UseNewCache() &&
|
||||
mLoadInfo->IsPrivate() &&
|
||||
mStorageName.EqualsLiteral("disk")) {
|
||||
// The cache v1 is storing all private entries in the memory-only
|
||||
// cache, so it would not be found in the v1 disk cache.
|
||||
mStorageName = NS_LITERAL_CSTRING("memory");
|
||||
}
|
||||
|
||||
return OpenCacheEntry();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAboutCacheEntry::Channel::OpenCacheEntry()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsICacheStorage> storage;
|
||||
rv = nsAboutCache::GetStorage(mStorageName, mLoadInfo, getter_AddRefs(storage));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// Invokes OnCacheEntryAvailable()
|
||||
rv = storage->AsyncOpenURI(mCacheURI, mEnhanceId,
|
||||
nsICacheStorage::OPEN_READONLY |
|
||||
nsICacheStorage::OPEN_SECRETLY,
|
||||
this);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAboutCacheEntry::Channel::ParseURI(nsIURI *uri,
|
||||
nsACString &storageName,
|
||||
nsILoadContextInfo **loadInfo,
|
||||
nsCString &enahnceID,
|
||||
nsIURI **cacheUri)
|
||||
{
|
||||
//
|
||||
// about:cache-entry?storage=[string]&contenxt=[string]&eid=[string]&uri=[string]
|
||||
//
|
||||
nsresult rv;
|
||||
|
||||
nsAutoCString path;
|
||||
rv = uri->GetPath(path);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsACString::const_iterator keyBegin, keyEnd, valBegin, begin, end;
|
||||
path.BeginReading(begin);
|
||||
path.EndReading(end);
|
||||
|
||||
keyBegin = begin; keyEnd = end;
|
||||
if (!FindInReadable(NS_LITERAL_CSTRING("?storage="), keyBegin, keyEnd))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
valBegin = keyEnd; // the value of the storage key starts after the key
|
||||
|
||||
keyBegin = keyEnd; keyEnd = end;
|
||||
if (!FindInReadable(NS_LITERAL_CSTRING("&context="), keyBegin, keyEnd))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
storageName.Assign(Substring(valBegin, keyBegin));
|
||||
valBegin = keyEnd; // the value of the context key starts after the key
|
||||
|
||||
keyBegin = keyEnd; keyEnd = end;
|
||||
if (!FindInReadable(NS_LITERAL_CSTRING("&eid="), keyBegin, keyEnd))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsAutoCString contextKey(Substring(valBegin, keyBegin));
|
||||
valBegin = keyEnd; // the value of the eid key starts after the key
|
||||
|
||||
keyBegin = keyEnd; keyEnd = end;
|
||||
if (!FindInReadable(NS_LITERAL_CSTRING("&uri="), keyBegin, keyEnd))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
enahnceID.Assign(Substring(valBegin, keyBegin));
|
||||
|
||||
valBegin = keyEnd; // the value of the uri key starts after the key
|
||||
nsAutoCString uriSpec(Substring(valBegin, end)); // uri is the last one
|
||||
|
||||
// Uf... parsing done, now get some objects from it...
|
||||
|
||||
nsCOMPtr<nsILoadContextInfo> info =
|
||||
CacheFileUtils::ParseKey(contextKey);
|
||||
if (!info)
|
||||
return NS_ERROR_FAILURE;
|
||||
info.forget(loadInfo);
|
||||
|
||||
rv = NS_NewURI(cacheUri, uriSpec);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsICacheEntryOpenCallback implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutCacheEntry::Channel::OnCacheEntryCheck(nsICacheEntry *aEntry,
|
||||
nsIApplicationCache *aApplicationCache,
|
||||
uint32_t *result)
|
||||
{
|
||||
*result = nsICacheEntryOpenCallback::ENTRY_WANTED;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutCacheEntry::Channel::OnCacheEntryAvailable(nsICacheEntry *entry,
|
||||
bool isNew,
|
||||
nsIApplicationCache *aApplicationCache,
|
||||
nsresult status)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
mWaitingForData = false;
|
||||
if (entry) {
|
||||
rv = WriteCacheEntryDescription(entry);
|
||||
} else if (!CacheObserver::UseNewCache() &&
|
||||
!mLoadInfo->IsPrivate() &&
|
||||
mStorageName.EqualsLiteral("memory")) {
|
||||
// If we were not able to find the entry in the memory storage
|
||||
// try again in the disk storage.
|
||||
// This is a workaround for cache v1: when an originally disk
|
||||
// cache entry is recreated as memory-only, it's clientID doesn't
|
||||
// change and we cannot find it in "HTTP-memory-only" session.
|
||||
// "Disk" cache storage looks at "HTTP".
|
||||
mStorageName = NS_LITERAL_CSTRING("disk");
|
||||
rv = OpenCacheEntry();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return NS_OK;
|
||||
}
|
||||
} else {
|
||||
rv = WriteCacheEntryUnavailable();
|
||||
}
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
|
||||
if (!mWaitingForData) {
|
||||
// Data is not expected, close the output of content now.
|
||||
CloseContent();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Print-out helper methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define APPEND_ROW(label, value) \
|
||||
PR_BEGIN_MACRO \
|
||||
buffer.AppendLiteral(" <tr>\n" \
|
||||
" <th>"); \
|
||||
buffer.AppendLiteral(label); \
|
||||
buffer.AppendLiteral(":</th>\n" \
|
||||
" <td>"); \
|
||||
buffer.Append(value); \
|
||||
buffer.AppendLiteral("</td>\n" \
|
||||
" </tr>\n"); \
|
||||
PR_END_MACRO
|
||||
|
||||
nsresult
|
||||
nsAboutCacheEntry::Channel::WriteCacheEntryDescription(nsICacheEntry *entry)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCString buffer;
|
||||
uint32_t n;
|
||||
|
||||
nsAutoCString str;
|
||||
|
||||
rv = entry->GetKey(str);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
buffer.SetCapacity(4096);
|
||||
buffer.AssignLiteral("<table>\n"
|
||||
" <tr>\n"
|
||||
" <th>key:</th>\n"
|
||||
" <td id=\"td-key\">");
|
||||
|
||||
// Test if the key is actually a URI
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
bool isJS = false;
|
||||
bool isData = false;
|
||||
|
||||
rv = NS_NewURI(getter_AddRefs(uri), str);
|
||||
// javascript: and data: URLs should not be linkified
|
||||
// since clicking them can cause scripts to run - bug 162584
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
uri->SchemeIs("javascript", &isJS);
|
||||
uri->SchemeIs("data", &isData);
|
||||
}
|
||||
char* escapedStr = nsEscapeHTML(str.get());
|
||||
if (NS_SUCCEEDED(rv) && !(isJS || isData)) {
|
||||
buffer.AppendLiteral("<a href=\"");
|
||||
buffer.Append(escapedStr);
|
||||
buffer.AppendLiteral("\">");
|
||||
buffer.Append(escapedStr);
|
||||
buffer.AppendLiteral("</a>");
|
||||
uri = nullptr;
|
||||
} else {
|
||||
buffer.Append(escapedStr);
|
||||
}
|
||||
free(escapedStr);
|
||||
buffer.AppendLiteral("</td>\n"
|
||||
" </tr>\n");
|
||||
|
||||
// temp vars for reporting
|
||||
char timeBuf[255];
|
||||
uint32_t u = 0;
|
||||
int32_t i = 0;
|
||||
nsAutoCString s;
|
||||
|
||||
// Fetch Count
|
||||
s.Truncate();
|
||||
entry->GetFetchCount(&i);
|
||||
s.AppendInt(i);
|
||||
APPEND_ROW("fetch count", s);
|
||||
|
||||
// Last Fetched
|
||||
entry->GetLastFetched(&u);
|
||||
if (u) {
|
||||
PrintTimeString(timeBuf, sizeof(timeBuf), u);
|
||||
APPEND_ROW("last fetched", timeBuf);
|
||||
} else {
|
||||
APPEND_ROW("last fetched", "No last fetch time (bug 1000338)");
|
||||
}
|
||||
|
||||
// Last Modified
|
||||
entry->GetLastModified(&u);
|
||||
if (u) {
|
||||
PrintTimeString(timeBuf, sizeof(timeBuf), u);
|
||||
APPEND_ROW("last modified", timeBuf);
|
||||
} else {
|
||||
APPEND_ROW("last modified", "No last modified time (bug 1000338)");
|
||||
}
|
||||
|
||||
// Expiration Time
|
||||
entry->GetExpirationTime(&u);
|
||||
if (u < 0xFFFFFFFF) {
|
||||
PrintTimeString(timeBuf, sizeof(timeBuf), u);
|
||||
APPEND_ROW("expires", timeBuf);
|
||||
} else {
|
||||
APPEND_ROW("expires", "No expiration time");
|
||||
}
|
||||
|
||||
// Data Size
|
||||
s.Truncate();
|
||||
uint32_t dataSize;
|
||||
if (NS_FAILED(entry->GetStorageDataSize(&dataSize)))
|
||||
dataSize = 0;
|
||||
s.AppendInt((int32_t)dataSize); // XXX nsICacheEntryInfo interfaces should be fixed.
|
||||
s.AppendLiteral(" B");
|
||||
APPEND_ROW("Data size", s);
|
||||
|
||||
// TODO - mayhemer
|
||||
// Here used to be a link to the disk file (in the old cache for entries that
|
||||
// did not fit any of the block files, in the new cache every time).
|
||||
// I'd rather have a small set of buttons here to action on the entry:
|
||||
// 1. save the content
|
||||
// 2. save as a complete HTTP response (response head, headers, content)
|
||||
// 3. doom the entry
|
||||
// A new bug(s) should be filed here.
|
||||
|
||||
// Security Info
|
||||
nsCOMPtr<nsISupports> securityInfo;
|
||||
entry->GetSecurityInfo(getter_AddRefs(securityInfo));
|
||||
if (securityInfo) {
|
||||
APPEND_ROW("Security", "This is a secure document.");
|
||||
} else {
|
||||
APPEND_ROW("Security",
|
||||
"This document does not have any security info associated with it.");
|
||||
}
|
||||
|
||||
buffer.AppendLiteral("</table>\n"
|
||||
"<hr/>\n"
|
||||
"<table>\n");
|
||||
|
||||
mBuffer = &buffer; // make it available for OnMetaDataElement().
|
||||
entry->VisitMetaData(this);
|
||||
mBuffer = nullptr;
|
||||
|
||||
buffer.AppendLiteral("</table>\n");
|
||||
mOutputStream->Write(buffer.get(), buffer.Length(), &n);
|
||||
buffer.Truncate();
|
||||
|
||||
// Provide a hexdump of the data
|
||||
if (!dataSize) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
entry->OpenInputStream(0, getter_AddRefs(stream));
|
||||
if (!stream) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<nsInputStreamPump> pump;
|
||||
rv = nsInputStreamPump::Create(getter_AddRefs(pump), stream);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_OK; // just ignore
|
||||
}
|
||||
|
||||
rv = pump->AsyncRead(this, nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_OK; // just ignore
|
||||
}
|
||||
|
||||
mWaitingForData = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAboutCacheEntry::Channel::WriteCacheEntryUnavailable()
|
||||
{
|
||||
uint32_t n;
|
||||
NS_NAMED_LITERAL_CSTRING(buffer,
|
||||
"The cache entry you selected is not available.");
|
||||
mOutputStream->Write(buffer.get(), buffer.Length(), &n);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsICacheEntryMetaDataVisitor implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutCacheEntry::Channel::OnMetaDataElement(char const * key, char const * value)
|
||||
{
|
||||
mBuffer->AppendLiteral(" <tr>\n"
|
||||
" <th>");
|
||||
mBuffer->Append(key);
|
||||
mBuffer->AppendLiteral(":</th>\n"
|
||||
" <td>");
|
||||
char* escapedValue = nsEscapeHTML(value);
|
||||
mBuffer->Append(escapedValue);
|
||||
free(escapedValue);
|
||||
mBuffer->AppendLiteral("</td>\n"
|
||||
" </tr>\n");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIStreamListener implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutCacheEntry::Channel::OnStartRequest(nsIRequest *request, nsISupports *ctx)
|
||||
{
|
||||
mHexDumpState = 0;
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(buffer, "<hr/>\n<pre>");
|
||||
uint32_t n;
|
||||
return mOutputStream->Write(buffer.get(), buffer.Length(), &n);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutCacheEntry::Channel::OnDataAvailable(nsIRequest *request, nsISupports *ctx,
|
||||
nsIInputStream *aInputStream,
|
||||
uint64_t aOffset,
|
||||
uint32_t aCount)
|
||||
{
|
||||
uint32_t n;
|
||||
return aInputStream->ReadSegments(
|
||||
&nsAboutCacheEntry::Channel::PrintCacheData, this, aCount, &n);
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
nsAboutCacheEntry::Channel::PrintCacheData(nsIInputStream *aInStream,
|
||||
void *aClosure,
|
||||
const char *aFromSegment,
|
||||
uint32_t aToOffset,
|
||||
uint32_t aCount,
|
||||
uint32_t *aWriteCount)
|
||||
{
|
||||
nsAboutCacheEntry::Channel *a =
|
||||
static_cast<nsAboutCacheEntry::Channel*>(aClosure);
|
||||
|
||||
nsCString buffer;
|
||||
HexDump(&a->mHexDumpState, aFromSegment, aCount, buffer);
|
||||
|
||||
uint32_t n;
|
||||
a->mOutputStream->Write(buffer.get(), buffer.Length(), &n);
|
||||
|
||||
*aWriteCount = aCount;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutCacheEntry::Channel::OnStopRequest(nsIRequest *request, nsISupports *ctx,
|
||||
nsresult result)
|
||||
{
|
||||
NS_NAMED_LITERAL_CSTRING(buffer, "</pre>\n");
|
||||
uint32_t n;
|
||||
mOutputStream->Write(buffer.get(), buffer.Length(), &n);
|
||||
|
||||
CloseContent();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsAboutCacheEntry::Channel::CloseContent()
|
||||
{
|
||||
NS_NAMED_LITERAL_CSTRING(buffer, "</body>\n</html>\n");
|
||||
uint32_t n;
|
||||
mOutputStream->Write(buffer.get(), buffer.Length(), &n);
|
||||
|
||||
mOutputStream->Close();
|
||||
mOutputStream = nullptr;
|
||||
}
|
||||
98
netwerk/protocol/about/nsAboutCacheEntry.h
Normal file
98
netwerk/protocol/about/nsAboutCacheEntry.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 nsAboutCacheEntry_h__
|
||||
#define nsAboutCacheEntry_h__
|
||||
|
||||
#include "nsIAboutModule.h"
|
||||
#include "nsICacheEntryOpenCallback.h"
|
||||
#include "nsICacheEntry.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsIAsyncOutputStream;
|
||||
class nsIInputStream;
|
||||
class nsILoadContextInfo;
|
||||
class nsIURI;
|
||||
class nsCString;
|
||||
|
||||
class nsAboutCacheEntry final : public nsIAboutModule
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIABOUTMODULE
|
||||
|
||||
private:
|
||||
virtual ~nsAboutCacheEntry() {}
|
||||
|
||||
class Channel final : public nsICacheEntryOpenCallback
|
||||
, public nsICacheEntryMetaDataVisitor
|
||||
, public nsIStreamListener
|
||||
, public nsIChannel
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICACHEENTRYOPENCALLBACK
|
||||
NS_DECL_NSICACHEENTRYMETADATAVISITOR
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_FORWARD_SAFE_NSICHANNEL(mChannel)
|
||||
NS_FORWARD_SAFE_NSIREQUEST(mChannel)
|
||||
|
||||
Channel()
|
||||
: mBuffer(nullptr)
|
||||
, mWaitingForData(false)
|
||||
, mHexDumpState(0)
|
||||
{}
|
||||
|
||||
private:
|
||||
virtual ~Channel() {}
|
||||
|
||||
public:
|
||||
MOZ_MUST_USE nsresult Init(nsIURI* uri, nsILoadInfo* aLoadInfo);
|
||||
|
||||
MOZ_MUST_USE nsresult GetContentStream(nsIURI *, nsIInputStream **);
|
||||
MOZ_MUST_USE nsresult OpenCacheEntry(nsIURI *);
|
||||
MOZ_MUST_USE nsresult OpenCacheEntry();
|
||||
MOZ_MUST_USE nsresult WriteCacheEntryDescription(nsICacheEntry *);
|
||||
MOZ_MUST_USE nsresult WriteCacheEntryUnavailable();
|
||||
MOZ_MUST_USE nsresult ParseURI(nsIURI *uri, nsACString &storageName,
|
||||
nsILoadContextInfo **loadInfo,
|
||||
nsCString &enahnceID,
|
||||
nsIURI **cacheUri);
|
||||
void CloseContent();
|
||||
|
||||
static MOZ_MUST_USE nsresult
|
||||
PrintCacheData(nsIInputStream *aInStream,
|
||||
void *aClosure,
|
||||
const char *aFromSegment,
|
||||
uint32_t aToOffset,
|
||||
uint32_t aCount,
|
||||
uint32_t *aWriteCount);
|
||||
|
||||
private:
|
||||
nsCString mStorageName, mEnhanceId;
|
||||
nsCOMPtr<nsILoadContextInfo> mLoadInfo;
|
||||
nsCOMPtr<nsIURI> mCacheURI;
|
||||
|
||||
nsCString *mBuffer;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mOutputStream;
|
||||
bool mWaitingForData;
|
||||
uint32_t mHexDumpState;
|
||||
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
};
|
||||
};
|
||||
|
||||
#define NS_ABOUT_CACHE_ENTRY_MODULE_CID \
|
||||
{ /* 7fa5237d-b0eb-438f-9e50-ca0166e63788 */ \
|
||||
0x7fa5237d, \
|
||||
0xb0eb, \
|
||||
0x438f, \
|
||||
{0x9e, 0x50, 0xca, 0x01, 0x66, 0xe6, 0x37, 0x88} \
|
||||
}
|
||||
|
||||
#endif // nsAboutCacheEntry_h__
|
||||
439
netwerk/protocol/about/nsAboutProtocolHandler.cpp
Normal file
439
netwerk/protocol/about/nsAboutProtocolHandler.cpp
Normal file
|
|
@ -0,0 +1,439 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
|
||||
#include "nsAboutProtocolHandler.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIAboutModule.h"
|
||||
#include "nsString.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsAboutProtocolUtils.h"
|
||||
#include "nsError.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIObjectInputStream.h"
|
||||
#include "nsIObjectOutputStream.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIWritablePropertyBag2.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIScriptError.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
|
||||
static NS_DEFINE_CID(kNestedAboutURICID, NS_NESTEDABOUTURI_CID);
|
||||
|
||||
static bool IsSafeForUntrustedContent(nsIAboutModule *aModule, nsIURI *aURI) {
|
||||
uint32_t flags;
|
||||
nsresult rv = aModule->GetURIFlags(aURI, &flags);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
return (flags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) != 0;
|
||||
}
|
||||
|
||||
static bool IsSafeToLinkForUntrustedContent(nsIAboutModule *aModule, nsIURI *aURI) {
|
||||
uint32_t flags;
|
||||
nsresult rv = aModule->GetURIFlags(aURI, &flags);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
return (flags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) && (flags & nsIAboutModule::MAKE_LINKABLE);
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsAboutProtocolHandler, nsIProtocolHandler,
|
||||
nsIProtocolHandlerWithDynamicFlags, nsISupportsWeakReference)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsIProtocolHandler methods:
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutProtocolHandler::GetScheme(nsACString &result)
|
||||
{
|
||||
result.AssignLiteral("about");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutProtocolHandler::GetDefaultPort(int32_t *result)
|
||||
{
|
||||
*result = -1; // no port for about: URLs
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutProtocolHandler::GetProtocolFlags(uint32_t *result)
|
||||
{
|
||||
*result = URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD | URI_SCHEME_NOT_SELF_LINKABLE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutProtocolHandler::GetFlagsForURI(nsIURI* aURI, uint32_t* aFlags)
|
||||
{
|
||||
// First use the default (which is "unsafe for content"):
|
||||
GetProtocolFlags(aFlags);
|
||||
|
||||
// Now try to see if this URI overrides the default:
|
||||
nsCOMPtr<nsIAboutModule> aboutMod;
|
||||
nsresult rv = NS_GetAboutModule(aURI, getter_AddRefs(aboutMod));
|
||||
if (NS_FAILED(rv)) {
|
||||
// Swallow this and just tell the consumer the default:
|
||||
return NS_OK;
|
||||
}
|
||||
uint32_t aboutModuleFlags = 0;
|
||||
rv = aboutMod->GetURIFlags(aURI, &aboutModuleFlags);
|
||||
// This should never happen, so pass back the error:
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Secure (https) pages can load safe about pages without becoming
|
||||
// mixed content.
|
||||
if (aboutModuleFlags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) {
|
||||
*aFlags |= URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT;
|
||||
// about: pages can only be loaded by unprivileged principals
|
||||
// if they are marked as LINKABLE
|
||||
if (aboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) {
|
||||
// Replace URI_DANGEROUS_TO_LOAD with URI_LOADABLE_BY_ANYONE.
|
||||
*aFlags &= ~URI_DANGEROUS_TO_LOAD;
|
||||
*aFlags |= URI_LOADABLE_BY_ANYONE;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutProtocolHandler::NewURI(const nsACString &aSpec,
|
||||
const char *aCharset, // ignore charset info
|
||||
nsIURI *aBaseURI,
|
||||
nsIURI **result)
|
||||
{
|
||||
*result = nullptr;
|
||||
nsresult rv;
|
||||
|
||||
// Use a simple URI to parse out some stuff first
|
||||
nsCOMPtr<nsIURI> url = do_CreateInstance(kSimpleURICID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = url->SetSpec(aSpec);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Unfortunately, people create random about: URIs that don't correspond to
|
||||
// about: modules... Since those URIs will never open a channel, might as
|
||||
// well consider them unsafe for better perf, and just in case.
|
||||
bool isSafe = false;
|
||||
|
||||
nsCOMPtr<nsIAboutModule> aboutMod;
|
||||
rv = NS_GetAboutModule(url, getter_AddRefs(aboutMod));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
isSafe = IsSafeToLinkForUntrustedContent(aboutMod, url);
|
||||
}
|
||||
|
||||
if (isSafe) {
|
||||
// We need to indicate that this baby is safe. Use an inner URI that
|
||||
// no one but the security manager will see. Make sure to preserve our
|
||||
// path, in case someone decides to hardcode checks for particular
|
||||
// about: URIs somewhere.
|
||||
nsAutoCString spec;
|
||||
rv = url->GetPath(spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
spec.Insert("moz-safe-about:", 0);
|
||||
|
||||
nsCOMPtr<nsIURI> inner;
|
||||
rv = NS_NewURI(getter_AddRefs(inner), spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsSimpleNestedURI* outer = new nsNestedAboutURI(inner, aBaseURI);
|
||||
NS_ENSURE_TRUE(outer, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// Take a ref to it in the COMPtr we plan to return
|
||||
url = outer;
|
||||
|
||||
rv = outer->SetSpec(aSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// We don't want to allow mutation, since it would allow safe and
|
||||
// unsafe URIs to change into each other...
|
||||
NS_TryToSetImmutable(url);
|
||||
url.swap(*result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutProtocolHandler::NewChannel2(nsIURI* uri,
|
||||
nsILoadInfo* aLoadInfo,
|
||||
nsIChannel** result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(uri);
|
||||
|
||||
// about:what you ask?
|
||||
nsCOMPtr<nsIAboutModule> aboutMod;
|
||||
nsresult rv = NS_GetAboutModule(uri, getter_AddRefs(aboutMod));
|
||||
|
||||
nsAutoCString path;
|
||||
nsresult rv2 = NS_GetAboutModuleName(uri, path);
|
||||
if (NS_SUCCEEDED(rv2) && path.EqualsLiteral("srcdoc")) {
|
||||
// about:srcdoc is meant to be unresolvable, yet is included in the
|
||||
// about lookup tables so that it can pass security checks when used in
|
||||
// a srcdoc iframe. To ensure that it stays unresolvable, we pretend
|
||||
// that it doesn't exist.
|
||||
rv = NS_ERROR_FACTORY_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// The standard return case:
|
||||
rv = aboutMod->NewChannel(uri, aLoadInfo, result);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Not all implementations of nsIAboutModule::NewChannel()
|
||||
// set the LoadInfo on the newly created channel yet, as
|
||||
// an interim solution we set the LoadInfo here if not
|
||||
// available on the channel. Bug 1087720
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = (*result)->GetLoadInfo();
|
||||
if (aLoadInfo != loadInfo) {
|
||||
if (loadInfo) {
|
||||
NS_ASSERTION(false,
|
||||
"nsIAboutModule->newChannel(aURI, aLoadInfo) needs to set LoadInfo");
|
||||
const char16_t* params[] = {
|
||||
u"nsIAboutModule->newChannel(aURI)",
|
||||
u"nsIAboutModule->newChannel(aURI, aLoadInfo)"
|
||||
};
|
||||
nsContentUtils::ReportToConsole(
|
||||
nsIScriptError::warningFlag,
|
||||
NS_LITERAL_CSTRING("Security by Default"),
|
||||
nullptr, // aDocument
|
||||
nsContentUtils::eNECKO_PROPERTIES,
|
||||
"APIDeprecationWarning",
|
||||
params, mozilla::ArrayLength(params));
|
||||
}
|
||||
(*result)->SetLoadInfo(aLoadInfo);
|
||||
}
|
||||
|
||||
// If this URI is safe for untrusted content, enforce that its
|
||||
// principal be based on the channel's originalURI by setting the
|
||||
// owner to null.
|
||||
// Note: this relies on aboutMod's newChannel implementation
|
||||
// having set the proper originalURI, which probably isn't ideal.
|
||||
if (IsSafeForUntrustedContent(aboutMod, uri)) {
|
||||
(*result)->SetOwner(nullptr);
|
||||
}
|
||||
|
||||
RefPtr<nsNestedAboutURI> aboutURI;
|
||||
nsresult rv2 = uri->QueryInterface(kNestedAboutURICID,
|
||||
getter_AddRefs(aboutURI));
|
||||
if (NS_SUCCEEDED(rv2) && aboutURI->GetBaseURI()) {
|
||||
nsCOMPtr<nsIWritablePropertyBag2> writableBag =
|
||||
do_QueryInterface(*result);
|
||||
if (writableBag) {
|
||||
writableBag->
|
||||
SetPropertyAsInterface(NS_LITERAL_STRING("baseURI"),
|
||||
aboutURI->GetBaseURI());
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
// mumble...
|
||||
|
||||
if (rv == NS_ERROR_FACTORY_NOT_REGISTERED) {
|
||||
// This looks like an about: we don't know about. Convert
|
||||
// this to an invalid URI error.
|
||||
rv = NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
|
||||
{
|
||||
return NewChannel2(uri, nullptr, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsAboutProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
|
||||
{
|
||||
// don't override anything.
|
||||
*_retval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Safe about protocol handler impl
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsSafeAboutProtocolHandler, nsIProtocolHandler, nsISupportsWeakReference)
|
||||
|
||||
// nsIProtocolHandler methods:
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSafeAboutProtocolHandler::GetScheme(nsACString &result)
|
||||
{
|
||||
result.AssignLiteral("moz-safe-about");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSafeAboutProtocolHandler::GetDefaultPort(int32_t *result)
|
||||
{
|
||||
*result = -1; // no port for moz-safe-about: URLs
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSafeAboutProtocolHandler::GetProtocolFlags(uint32_t *result)
|
||||
{
|
||||
*result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSafeAboutProtocolHandler::NewURI(const nsACString &aSpec,
|
||||
const char *aCharset, // ignore charset info
|
||||
nsIURI *aBaseURI,
|
||||
nsIURI **result)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIURI> url = do_CreateInstance(kSimpleURICID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = url->SetSpec(aSpec);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_TryToSetImmutable(url);
|
||||
|
||||
*result = nullptr;
|
||||
url.swap(*result);
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSafeAboutProtocolHandler::NewChannel2(nsIURI* uri,
|
||||
nsILoadInfo* aLoadInfo,
|
||||
nsIChannel** result)
|
||||
{
|
||||
*result = nullptr;
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSafeAboutProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
|
||||
{
|
||||
*result = nullptr;
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSafeAboutProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
|
||||
{
|
||||
// don't override anything.
|
||||
*_retval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// nsNestedAboutURI implementation
|
||||
NS_INTERFACE_MAP_BEGIN(nsNestedAboutURI)
|
||||
if (aIID.Equals(kNestedAboutURICID))
|
||||
foundInterface = static_cast<nsIURI*>(this);
|
||||
else
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsSimpleNestedURI)
|
||||
|
||||
// nsISerializable
|
||||
NS_IMETHODIMP
|
||||
nsNestedAboutURI::Read(nsIObjectInputStream* aStream)
|
||||
{
|
||||
nsresult rv = nsSimpleNestedURI::Read(aStream);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
bool haveBase;
|
||||
rv = aStream->ReadBoolean(&haveBase);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (haveBase) {
|
||||
nsCOMPtr<nsISupports> supports;
|
||||
rv = aStream->ReadObject(true, getter_AddRefs(supports));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
mBaseURI = do_QueryInterface(supports, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNestedAboutURI::Write(nsIObjectOutputStream* aStream)
|
||||
{
|
||||
nsresult rv = nsSimpleNestedURI::Write(aStream);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = aStream->WriteBoolean(mBaseURI != nullptr);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (mBaseURI) {
|
||||
// A previous iteration of this code wrote out mBaseURI as nsISupports
|
||||
// and then read it in as nsIURI, which is non-kosher when mBaseURI
|
||||
// implements more than just a single line of interfaces and the
|
||||
// canonical nsISupports* isn't the one a static_cast<> of mBaseURI
|
||||
// would produce. For backwards compatibility with existing
|
||||
// serializations we continue to write mBaseURI as nsISupports but
|
||||
// switch to reading it as nsISupports, with a post-read QI to get to
|
||||
// nsIURI.
|
||||
rv = aStream->WriteCompoundObject(mBaseURI, NS_GET_IID(nsISupports),
|
||||
true);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsSimpleURI
|
||||
/* virtual */ nsSimpleURI*
|
||||
nsNestedAboutURI::StartClone(nsSimpleURI::RefHandlingEnum aRefHandlingMode,
|
||||
const nsACString& aNewRef)
|
||||
{
|
||||
// Sadly, we can't make use of nsSimpleNestedURI::StartClone here.
|
||||
// However, this function is expected to exactly match that function,
|
||||
// aside from the "new ns***URI()" call.
|
||||
NS_ENSURE_TRUE(mInnerURI, nullptr);
|
||||
|
||||
nsCOMPtr<nsIURI> innerClone;
|
||||
nsresult rv;
|
||||
if (aRefHandlingMode == eHonorRef) {
|
||||
rv = mInnerURI->Clone(getter_AddRefs(innerClone));
|
||||
} else if (aRefHandlingMode == eReplaceRef) {
|
||||
rv = mInnerURI->CloneWithNewRef(aNewRef, getter_AddRefs(innerClone));
|
||||
} else {
|
||||
rv = mInnerURI->CloneIgnoringRef(getter_AddRefs(innerClone));
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsNestedAboutURI* url = new nsNestedAboutURI(innerClone, mBaseURI);
|
||||
SetRefOnClone(url, aRefHandlingMode, aNewRef);
|
||||
url->SetMutable(false);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
// nsIClassInfo
|
||||
NS_IMETHODIMP
|
||||
nsNestedAboutURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
|
||||
{
|
||||
*aClassIDNoAlloc = kNestedAboutURICID;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
90
netwerk/protocol/about/nsAboutProtocolHandler.h
Normal file
90
netwerk/protocol/about/nsAboutProtocolHandler.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 nsAboutProtocolHandler_h___
|
||||
#define nsAboutProtocolHandler_h___
|
||||
|
||||
#include "nsIProtocolHandler.h"
|
||||
#include "nsSimpleNestedURI.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class nsIURI;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class nsAboutProtocolHandler : public nsIProtocolHandlerWithDynamicFlags
|
||||
, public nsIProtocolHandler
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIProtocolHandler methods:
|
||||
NS_DECL_NSIPROTOCOLHANDLER
|
||||
NS_DECL_NSIPROTOCOLHANDLERWITHDYNAMICFLAGS
|
||||
|
||||
// nsAboutProtocolHandler methods:
|
||||
nsAboutProtocolHandler() {}
|
||||
|
||||
private:
|
||||
virtual ~nsAboutProtocolHandler() {}
|
||||
};
|
||||
|
||||
class nsSafeAboutProtocolHandler final : public nsIProtocolHandler
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIProtocolHandler methods:
|
||||
NS_DECL_NSIPROTOCOLHANDLER
|
||||
|
||||
// nsSafeAboutProtocolHandler methods:
|
||||
nsSafeAboutProtocolHandler() {}
|
||||
|
||||
private:
|
||||
~nsSafeAboutProtocolHandler() {}
|
||||
};
|
||||
|
||||
|
||||
// Class to allow us to propagate the base URI to about:blank correctly
|
||||
class nsNestedAboutURI : public nsSimpleNestedURI {
|
||||
public:
|
||||
nsNestedAboutURI(nsIURI* aInnerURI, nsIURI* aBaseURI)
|
||||
: nsSimpleNestedURI(aInnerURI)
|
||||
, mBaseURI(aBaseURI)
|
||||
{}
|
||||
|
||||
// For use only from deserialization
|
||||
nsNestedAboutURI() : nsSimpleNestedURI() {}
|
||||
|
||||
virtual ~nsNestedAboutURI() {}
|
||||
|
||||
// Override QI so we can QI to our CID as needed
|
||||
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
|
||||
|
||||
// Override StartClone(), the nsISerializable methods, and
|
||||
// GetClassIDNoAlloc; this last is needed to make our nsISerializable impl
|
||||
// work right.
|
||||
virtual nsSimpleURI* StartClone(RefHandlingEnum aRefHandlingMode,
|
||||
const nsACString& newRef);
|
||||
NS_IMETHOD Read(nsIObjectInputStream* aStream);
|
||||
NS_IMETHOD Write(nsIObjectOutputStream* aStream);
|
||||
NS_IMETHOD GetClassIDNoAlloc(nsCID *aClassIDNoAlloc);
|
||||
|
||||
nsIURI* GetBaseURI() const {
|
||||
return mBaseURI;
|
||||
}
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsIURI> mBaseURI;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* nsAboutProtocolHandler_h___ */
|
||||
71
netwerk/protocol/about/nsAboutProtocolUtils.h
Normal file
71
netwerk/protocol/about/nsAboutProtocolUtils.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/* 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 nsAboutProtocolUtils_h
|
||||
#define nsAboutProtocolUtils_h
|
||||
|
||||
#include "nsIURI.h"
|
||||
#include "nsString.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsIAboutModule.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "prtime.h"
|
||||
|
||||
inline MOZ_MUST_USE nsresult
|
||||
NS_GetAboutModuleName(nsIURI *aAboutURI, nsCString& aModule)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
{
|
||||
bool isAbout;
|
||||
NS_ASSERTION(NS_SUCCEEDED(aAboutURI->SchemeIs("about", &isAbout)) &&
|
||||
isAbout,
|
||||
"should be used only on about: URIs");
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult rv = aAboutURI->GetPath(aModule);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
int32_t f = aModule.FindCharInSet(NS_LITERAL_CSTRING("#?"));
|
||||
if (f != kNotFound) {
|
||||
aModule.Truncate(f);
|
||||
}
|
||||
|
||||
// convert to lowercase, as all about: modules are lowercase
|
||||
ToLowerCase(aModule);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
inline nsresult
|
||||
NS_GetAboutModule(nsIURI *aAboutURI, nsIAboutModule** aModule)
|
||||
{
|
||||
NS_PRECONDITION(aAboutURI, "Must have URI");
|
||||
|
||||
nsAutoCString contractID;
|
||||
nsresult rv = NS_GetAboutModuleName(aAboutURI, contractID);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// look up a handler to deal with "what"
|
||||
contractID.Insert(NS_LITERAL_CSTRING(NS_ABOUT_MODULE_CONTRACTID_PREFIX), 0);
|
||||
|
||||
return CallGetService(contractID.get(), aModule);
|
||||
}
|
||||
|
||||
inline PRTime SecondsToPRTime(uint32_t t_sec)
|
||||
{
|
||||
PRTime t_usec, usec_per_sec;
|
||||
t_usec = t_sec;
|
||||
usec_per_sec = PR_USEC_PER_SEC;
|
||||
return t_usec *= usec_per_sec;
|
||||
}
|
||||
inline void PrintTimeString(char *buf, uint32_t bufsize, uint32_t t_sec)
|
||||
{
|
||||
PRExplodedTime et;
|
||||
PRTime t_usec = SecondsToPRTime(t_sec);
|
||||
PR_ExplodeTime(t_usec, PR_LocalTimeParameters, &et);
|
||||
PR_FormatTime(buf, bufsize, "%Y-%m-%d %H:%M:%S", &et);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
88
netwerk/protocol/about/nsIAboutModule.idl
Normal file
88
netwerk/protocol/about/nsIAboutModule.idl
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIURI;
|
||||
interface nsIChannel;
|
||||
interface nsILoadInfo;
|
||||
|
||||
[scriptable, uuid(c0c19db9-1b5a-4ac5-b656-ed6f8149fa48)]
|
||||
interface nsIAboutModule : nsISupports
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs a new channel for the about protocol module.
|
||||
*
|
||||
* @param aURI the uri of the new channel
|
||||
* @param aLoadInfo the loadinfo of the new channel
|
||||
*/
|
||||
nsIChannel newChannel(in nsIURI aURI,
|
||||
in nsILoadInfo aLoadInfo);
|
||||
|
||||
/**
|
||||
* A flag that indicates whether a URI should be run with content
|
||||
* privileges. If it is, the about: protocol handler will enforce that
|
||||
* the principal of channels created for it be based on their
|
||||
* originalURI or URI (depending on the channel flags), by setting
|
||||
* their "owner" to null.
|
||||
* If content needs to be able to link to this URI, specify
|
||||
* URI_CONTENT_LINKABLE as well.
|
||||
*/
|
||||
const unsigned long URI_SAFE_FOR_UNTRUSTED_CONTENT = (1 << 0);
|
||||
|
||||
/**
|
||||
* A flag that indicates whether script should be enabled for the
|
||||
* given about: URI even if it's disabled in general.
|
||||
*/
|
||||
const unsigned long ALLOW_SCRIPT = (1 << 1);
|
||||
|
||||
/**
|
||||
* A flag that indicates whether this about: URI doesn't want to be listed
|
||||
* in about:about, especially if it's not useful without a query string.
|
||||
*/
|
||||
const unsigned long HIDE_FROM_ABOUTABOUT = (1 << 2);
|
||||
|
||||
/**
|
||||
* A flag that indicates whether this about: URI wants Indexed DB enabled.
|
||||
*/
|
||||
const unsigned long ENABLE_INDEXED_DB = (1 << 3);
|
||||
|
||||
/**
|
||||
* A flag that indicates that this URI can be loaded in a child process
|
||||
*/
|
||||
const unsigned long URI_CAN_LOAD_IN_CHILD = (1 << 4);
|
||||
|
||||
/**
|
||||
* A flag that indicates that this URI must be loaded in a child process
|
||||
*/
|
||||
const unsigned long URI_MUST_LOAD_IN_CHILD = (1 << 5);
|
||||
|
||||
/**
|
||||
* Obsolete. This flag no longer has any effect and will be removed in future.
|
||||
*/
|
||||
const unsigned long MAKE_UNLINKABLE = (1 << 6);
|
||||
|
||||
/**
|
||||
* A flag that indicates that this URI should be linkable from content.
|
||||
* Ignored unless URI_SAFE_FOR_UNTRUSTED_CONTENT is also specified.
|
||||
*/
|
||||
const unsigned long MAKE_LINKABLE = (1 << 7);
|
||||
|
||||
/**
|
||||
* A method to get the flags that apply to a given about: URI. The URI
|
||||
* passed in is guaranteed to be one of the URIs that this module
|
||||
* registered to deal with.
|
||||
*/
|
||||
unsigned long getURIFlags(in nsIURI aURI);
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
||||
#define NS_ABOUT_MODULE_CONTRACTID "@mozilla.org/network/protocol/about;1"
|
||||
#define NS_ABOUT_MODULE_CONTRACTID_PREFIX NS_ABOUT_MODULE_CONTRACTID "?what="
|
||||
#define NS_ABOUT_MODULE_CONTRACTID_LENGTH 49 // strlen(NS_ABOUT_MODULE_CONTRACTID_PREFIX)
|
||||
|
||||
%}
|
||||
77
netwerk/protocol/data/DataChannelChild.cpp
Normal file
77
netwerk/protocol/data/DataChannelChild.cpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=4 sw=4 sts=4 et 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/. */
|
||||
|
||||
#include "DataChannelChild.h"
|
||||
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(DataChannelChild, nsDataChannel, nsIChildChannel)
|
||||
|
||||
DataChannelChild::DataChannelChild(nsIURI* aURI)
|
||||
: nsDataChannel(aURI)
|
||||
, mIPCOpen(false)
|
||||
{
|
||||
}
|
||||
|
||||
DataChannelChild::~DataChannelChild()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DataChannelChild::ConnectParent(uint32_t aId)
|
||||
{
|
||||
if (!gNeckoChild->SendPDataChannelConstructor(this, aId)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// IPC now has a ref to us.
|
||||
AddIPDLReference();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DataChannelChild::CompleteRedirectSetup(nsIStreamListener *aListener,
|
||||
nsISupports *aContext)
|
||||
{
|
||||
nsresult rv;
|
||||
if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
|
||||
MOZ_ASSERT(!aContext, "aContext should be null!");
|
||||
rv = AsyncOpen2(aListener);
|
||||
}
|
||||
else {
|
||||
rv = AsyncOpen(aListener, aContext);
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mIPCOpen) {
|
||||
Unused << Send__delete__(this);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DataChannelChild::AddIPDLReference()
|
||||
{
|
||||
AddRef();
|
||||
mIPCOpen = true;
|
||||
}
|
||||
|
||||
void
|
||||
DataChannelChild::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
MOZ_ASSERT(mIPCOpen);
|
||||
mIPCOpen = false;
|
||||
Release();
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
43
netwerk/protocol/data/DataChannelChild.h
Normal file
43
netwerk/protocol/data/DataChannelChild.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=4 sw=4 sts=4 et 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 NS_DATACHANNELCHILD_H
|
||||
#define NS_DATACHANNELCHILD_H
|
||||
|
||||
#include "nsDataChannel.h"
|
||||
#include "nsIChildChannel.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
#include "mozilla/net/PDataChannelChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class DataChannelChild : public nsDataChannel
|
||||
, public nsIChildChannel
|
||||
, public PDataChannelChild
|
||||
{
|
||||
public:
|
||||
explicit DataChannelChild(nsIURI *uri);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSICHILDCHANNEL
|
||||
|
||||
protected:
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
|
||||
private:
|
||||
~DataChannelChild();
|
||||
|
||||
void AddIPDLReference();
|
||||
|
||||
bool mIPCOpen;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* NS_DATACHANNELCHILD_H */
|
||||
89
netwerk/protocol/data/DataChannelParent.cpp
Normal file
89
netwerk/protocol/data/DataChannelParent.cpp
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=4 sw=4 sts=4 et 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/. */
|
||||
|
||||
#include "DataChannelParent.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIChannel.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
NS_IMPL_ISUPPORTS(DataChannelParent, nsIParentChannel, nsIStreamListener)
|
||||
|
||||
DataChannelParent::~DataChannelParent()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
DataChannelParent::Init(const uint32_t &channelId)
|
||||
{
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DataChannelParent::SetParentListener(HttpChannelParentListener* aListener)
|
||||
{
|
||||
// Nothing to do.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DataChannelParent::NotifyTrackingProtectionDisabled()
|
||||
{
|
||||
// Nothing to do.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DataChannelParent::Delete()
|
||||
{
|
||||
// Nothing to do.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DataChannelParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DataChannelParent::OnStartRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext)
|
||||
{
|
||||
// We don't have a way to prevent nsBaseChannel from calling AsyncOpen on
|
||||
// the created nsDataChannel. We don't have anywhere to send the data in the
|
||||
// parent, so abort the binding.
|
||||
return NS_BINDING_ABORTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DataChannelParent::OnStopRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsresult aStatusCode)
|
||||
{
|
||||
// See above.
|
||||
MOZ_ASSERT(NS_FAILED(aStatusCode));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DataChannelParent::OnDataAvailable(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsIInputStream *aInputStream,
|
||||
uint64_t aOffset,
|
||||
uint32_t aCount)
|
||||
{
|
||||
// See above.
|
||||
MOZ_CRASH("Should never be called");
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
41
netwerk/protocol/data/DataChannelParent.h
Normal file
41
netwerk/protocol/data/DataChannelParent.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=4 sw=4 sts=4 et 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 NS_DATACHANNELPARENT_H
|
||||
#define NS_DATACHANNELPARENT_H
|
||||
|
||||
#include "nsIParentChannel.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
#include "mozilla/net/PDataChannelParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
// In order to support HTTP redirects to data:, we need to implement the HTTP
|
||||
// redirection API, which requires a class that implements nsIParentChannel
|
||||
// and which calls NS_LinkRedirectChannels.
|
||||
class DataChannelParent : public nsIParentChannel
|
||||
, public PDataChannelParent
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPARENTCHANNEL
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
|
||||
MOZ_MUST_USE bool Init(const uint32_t& aArgs);
|
||||
|
||||
private:
|
||||
~DataChannelParent();
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* NS_DATACHANNELPARENT_H */
|
||||
24
netwerk/protocol/data/moz.build
Normal file
24
netwerk/protocol/data/moz.build
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
EXPORTS.mozilla.net += [
|
||||
'DataChannelParent.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'DataChannelChild.cpp',
|
||||
'DataChannelParent.cpp',
|
||||
'nsDataChannel.cpp',
|
||||
'nsDataHandler.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
LOCAL_INCLUDES += [
|
||||
'/netwerk/base',
|
||||
]
|
||||
|
||||
91
netwerk/protocol/data/nsDataChannel.cpp
Normal file
91
netwerk/protocol/data/nsDataChannel.cpp
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
// data implementation
|
||||
|
||||
#include "nsDataChannel.h"
|
||||
|
||||
#include "mozilla/Base64.h"
|
||||
#include "nsIOService.h"
|
||||
#include "nsDataHandler.h"
|
||||
#include "nsIPipe.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsEscape.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
nsresult
|
||||
nsDataChannel::OpenContentStream(bool async, nsIInputStream **result,
|
||||
nsIChannel** channel)
|
||||
{
|
||||
NS_ENSURE_TRUE(URI(), NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsAutoCString spec;
|
||||
rv = URI()->GetAsciiSpec(spec);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCString contentType, contentCharset, dataBuffer;
|
||||
bool lBase64;
|
||||
rv = nsDataHandler::ParseURI(spec, contentType, &contentCharset,
|
||||
lBase64, &dataBuffer);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
NS_UnescapeURL(dataBuffer);
|
||||
|
||||
if (lBase64) {
|
||||
// Don't allow spaces in base64-encoded content. This is only
|
||||
// relevant for escaped spaces; other spaces are stripped in
|
||||
// NewURI.
|
||||
dataBuffer.StripWhitespace();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> bufInStream;
|
||||
nsCOMPtr<nsIOutputStream> bufOutStream;
|
||||
|
||||
// create an unbounded pipe.
|
||||
rv = NS_NewPipe(getter_AddRefs(bufInStream),
|
||||
getter_AddRefs(bufOutStream),
|
||||
nsIOService::gDefaultSegmentSize,
|
||||
UINT32_MAX,
|
||||
async, true);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
uint32_t contentLen;
|
||||
if (lBase64) {
|
||||
const uint32_t dataLen = dataBuffer.Length();
|
||||
int32_t resultLen = 0;
|
||||
if (dataLen >= 1 && dataBuffer[dataLen-1] == '=') {
|
||||
if (dataLen >= 2 && dataBuffer[dataLen-2] == '=')
|
||||
resultLen = dataLen-2;
|
||||
else
|
||||
resultLen = dataLen-1;
|
||||
} else {
|
||||
resultLen = dataLen;
|
||||
}
|
||||
resultLen = ((resultLen * 3) / 4);
|
||||
|
||||
nsAutoCString decodedData;
|
||||
rv = Base64Decode(dataBuffer, decodedData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = bufOutStream->Write(decodedData.get(), resultLen, &contentLen);
|
||||
} else {
|
||||
rv = bufOutStream->Write(dataBuffer.get(), dataBuffer.Length(), &contentLen);
|
||||
}
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
SetContentType(contentType);
|
||||
SetContentCharset(contentCharset);
|
||||
mContentLength = contentLen;
|
||||
|
||||
bufInStream.forget(result);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
28
netwerk/protocol/data/nsDataChannel.h
Normal file
28
netwerk/protocol/data/nsDataChannel.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
// data implementation header
|
||||
|
||||
#ifndef nsDataChannel_h___
|
||||
#define nsDataChannel_h___
|
||||
|
||||
#include "nsBaseChannel.h"
|
||||
|
||||
class nsIInputStream;
|
||||
|
||||
class nsDataChannel : public nsBaseChannel
|
||||
{
|
||||
public:
|
||||
explicit nsDataChannel(nsIURI *uri) {
|
||||
SetURI(uri);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual MOZ_MUST_USE nsresult OpenContentStream(bool async,
|
||||
nsIInputStream **result,
|
||||
nsIChannel** channel);
|
||||
};
|
||||
|
||||
#endif /* nsDataChannel_h___ */
|
||||
250
netwerk/protocol/data/nsDataHandler.cpp
Normal file
250
netwerk/protocol/data/nsDataHandler.cpp
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsDataChannel.h"
|
||||
#include "nsDataHandler.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsError.h"
|
||||
#include "DataChannelChild.h"
|
||||
#include "plstr.h"
|
||||
|
||||
static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
nsDataHandler::nsDataHandler() {
|
||||
}
|
||||
|
||||
nsDataHandler::~nsDataHandler() {
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsDataHandler, nsIProtocolHandler, nsISupportsWeakReference)
|
||||
|
||||
nsresult
|
||||
nsDataHandler::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) {
|
||||
|
||||
nsDataHandler* ph = new nsDataHandler();
|
||||
if (ph == nullptr)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(ph);
|
||||
nsresult rv = ph->QueryInterface(aIID, aResult);
|
||||
NS_RELEASE(ph);
|
||||
return rv;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsIProtocolHandler methods:
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDataHandler::GetScheme(nsACString &result) {
|
||||
result.AssignLiteral("data");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDataHandler::GetDefaultPort(int32_t *result) {
|
||||
// no ports for data protocol
|
||||
*result = -1;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDataHandler::GetProtocolFlags(uint32_t *result) {
|
||||
*result = URI_NORELATIVE | URI_NOAUTH | URI_INHERITS_SECURITY_CONTEXT |
|
||||
URI_LOADABLE_BY_ANYONE | URI_NON_PERSISTABLE | URI_IS_LOCAL_RESOURCE |
|
||||
URI_SYNC_LOAD_IS_OK;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDataHandler::NewURI(const nsACString &aSpec,
|
||||
const char *aCharset, // ignore charset info
|
||||
nsIURI *aBaseURI,
|
||||
nsIURI **result) {
|
||||
nsresult rv;
|
||||
RefPtr<nsIURI> uri;
|
||||
|
||||
nsCString spec(aSpec);
|
||||
|
||||
if (aBaseURI && !spec.IsEmpty() && spec[0] == '#') {
|
||||
// Looks like a reference instead of a fully-specified URI.
|
||||
// --> initialize |uri| as a clone of |aBaseURI|, with ref appended.
|
||||
rv = aBaseURI->Clone(getter_AddRefs(uri));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
rv = uri->SetRef(spec);
|
||||
} else {
|
||||
// Otherwise, we'll assume |spec| is a fully-specified data URI
|
||||
nsAutoCString contentType;
|
||||
bool base64;
|
||||
rv = ParseURI(spec, contentType, /* contentCharset = */ nullptr,
|
||||
base64, /* dataBuffer = */ nullptr);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// Strip whitespace unless this is text, where whitespace is important
|
||||
// Don't strip escaped whitespace though (bug 391951)
|
||||
if (base64 || (strncmp(contentType.get(),"text/",5) != 0 &&
|
||||
contentType.Find("xml") == kNotFound)) {
|
||||
// it's ascii encoded binary, don't let any spaces in
|
||||
if (!spec.StripWhitespace(mozilla::fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
uri = do_CreateInstance(kSimpleURICID, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
rv = uri->SetSpec(spec);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
uri.forget(result);
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDataHandler::NewChannel2(nsIURI* uri,
|
||||
nsILoadInfo* aLoadInfo,
|
||||
nsIChannel** result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(uri);
|
||||
nsDataChannel* channel;
|
||||
if (XRE_IsParentProcess()) {
|
||||
channel = new nsDataChannel(uri);
|
||||
} else {
|
||||
channel = new mozilla::net::DataChannelChild(uri);
|
||||
}
|
||||
NS_ADDREF(channel);
|
||||
|
||||
nsresult rv = channel->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_RELEASE(channel);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// set the loadInfo on the new channel
|
||||
rv = channel->SetLoadInfo(aLoadInfo);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_RELEASE(channel);
|
||||
return rv;
|
||||
}
|
||||
|
||||
*result = channel;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDataHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
|
||||
{
|
||||
return NewChannel2(uri, nullptr, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDataHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) {
|
||||
// don't override anything.
|
||||
*_retval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#define BASE64_EXTENSION ";base64"
|
||||
|
||||
nsresult
|
||||
nsDataHandler::ParseURI(nsCString& spec,
|
||||
nsCString& contentType,
|
||||
nsCString* contentCharset,
|
||||
bool& isBase64,
|
||||
nsCString* dataBuffer)
|
||||
{
|
||||
isBase64 = false;
|
||||
|
||||
// move past "data:"
|
||||
const char* roBuffer = (const char*) PL_strcasestr(spec.get(), "data:");
|
||||
if (!roBuffer) {
|
||||
// malformed uri
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
roBuffer += sizeof("data:") - 1;
|
||||
|
||||
// First, find the start of the data
|
||||
const char* roComma = strchr(roBuffer, ',');
|
||||
const char* roHash = strchr(roBuffer, '#');
|
||||
if (!roComma || (roHash && roHash < roComma)) {
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
}
|
||||
|
||||
if (roComma == roBuffer) {
|
||||
// nothing but data
|
||||
contentType.AssignLiteral("text/plain");
|
||||
if (contentCharset) {
|
||||
contentCharset->AssignLiteral("US-ASCII");
|
||||
}
|
||||
} else {
|
||||
// Make a copy of the non-data part so we can null out parts of it as
|
||||
// we go. This copy will be a small number of chars, in contrast to the
|
||||
// data which may be large.
|
||||
char* buffer = PL_strndup(roBuffer, roComma - roBuffer);
|
||||
|
||||
// determine if the data is base64 encoded.
|
||||
char* base64 = PL_strcasestr(buffer, BASE64_EXTENSION);
|
||||
if (base64) {
|
||||
char *beyond = base64 + sizeof(BASE64_EXTENSION) - 1;
|
||||
// Per the RFC 2397 grammar, "base64" MUST be at the end of the
|
||||
// non-data part.
|
||||
//
|
||||
// But we also allow it in between parameters so a subsequent ";"
|
||||
// is ok as well (this deals with *broken* data URIs, see bug
|
||||
// 781693 for an example). Anything after "base64" in the non-data
|
||||
// part will be discarded in this case, however.
|
||||
if (*beyond == '\0' || *beyond == ';') {
|
||||
isBase64 = true;
|
||||
*base64 = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
// everything else is content type
|
||||
char *semiColon = (char *) strchr(buffer, ';');
|
||||
if (semiColon)
|
||||
*semiColon = '\0';
|
||||
|
||||
if (semiColon == buffer || base64 == buffer) {
|
||||
// there is no content type, but there are other parameters
|
||||
contentType.AssignLiteral("text/plain");
|
||||
} else {
|
||||
contentType.Assign(buffer);
|
||||
ToLowerCase(contentType);
|
||||
if (!contentType.StripWhitespace(mozilla::fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
if (semiColon && contentCharset) {
|
||||
char *charset = PL_strcasestr(semiColon + 1, "charset=");
|
||||
if (charset) {
|
||||
contentCharset->Assign(charset + sizeof("charset=") - 1);
|
||||
if (!contentCharset->StripWhitespace(mozilla::fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
if (dataBuffer) {
|
||||
// Split encoded data from terminal "#ref" (if present)
|
||||
const char* roData = roComma + 1;
|
||||
bool ok = !roHash
|
||||
? dataBuffer->Assign(roData, mozilla::fallible)
|
||||
: dataBuffer->Assign(roData, roHash - roData, mozilla::fallible);
|
||||
if (!ok) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
41
netwerk/protocol/data/nsDataHandler.h
Normal file
41
netwerk/protocol/data/nsDataHandler.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 nsDataHandler_h___
|
||||
#define nsDataHandler_h___
|
||||
|
||||
#include "nsIProtocolHandler.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
class nsDataHandler : public nsIProtocolHandler
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
virtual ~nsDataHandler();
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIProtocolHandler methods:
|
||||
NS_DECL_NSIPROTOCOLHANDLER
|
||||
|
||||
// nsDataHandler methods:
|
||||
nsDataHandler();
|
||||
|
||||
// Define a Create method to be used with a factory:
|
||||
static MOZ_MUST_USE nsresult
|
||||
Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult);
|
||||
|
||||
// Parse a data: URI and return the individual parts
|
||||
// (the given spec will temporarily be modified but will be returned
|
||||
// to the original before returning)
|
||||
// contentCharset and dataBuffer can be nullptr if they are not needed.
|
||||
static MOZ_MUST_USE nsresult ParseURI(nsCString& spec,
|
||||
nsCString& contentType,
|
||||
nsCString* contentCharset,
|
||||
bool& isBase64,
|
||||
nsCString* dataBuffer);
|
||||
};
|
||||
|
||||
#endif /* nsDataHandler_h___ */
|
||||
21
netwerk/protocol/data/nsDataModule.cpp
Normal file
21
netwerk/protocol/data/nsDataModule.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsIModule.h"
|
||||
#include "nsIGenericFactory.h"
|
||||
#include "nsDataHandler.h"
|
||||
|
||||
// The list of components we register
|
||||
static const nsModuleComponentInfo components[] = {
|
||||
{ "Data Protocol Handler",
|
||||
NS_DATAHANDLER_CID,
|
||||
NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "data",
|
||||
nsDataHandler::Create},
|
||||
};
|
||||
|
||||
NS_IMPL_NSGETMODULE(nsDataProtocolModule, components)
|
||||
|
||||
|
||||
|
||||
301
netwerk/protocol/device/AndroidCaptureProvider.cpp
Normal file
301
netwerk/protocol/device/AndroidCaptureProvider.cpp
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "AndroidCaptureProvider.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsMemory.h"
|
||||
#include "RawStructs.h"
|
||||
|
||||
// The maximum number of frames we keep in our queue. Don't live in the past.
|
||||
#define MAX_FRAMES_QUEUED 10
|
||||
|
||||
using namespace mozilla::net;
|
||||
|
||||
NS_IMPL_ISUPPORTS(AndroidCameraInputStream, nsIInputStream, nsIAsyncInputStream)
|
||||
|
||||
AndroidCameraInputStream::AndroidCameraInputStream() :
|
||||
mWidth(0), mHeight(0), mCamera(0), mHeaderSent(false), mClosed(true), mFrameSize(0),
|
||||
mMonitor("AndroidCamera.Monitor")
|
||||
{
|
||||
mAvailable = sizeof(RawVideoHeader);
|
||||
mFrameQueue = new nsDeque();
|
||||
}
|
||||
|
||||
AndroidCameraInputStream::~AndroidCameraInputStream() {
|
||||
// clear the frame queue
|
||||
while (mFrameQueue->GetSize() > 0) {
|
||||
free(mFrameQueue->PopFront());
|
||||
}
|
||||
delete mFrameQueue;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AndroidCameraInputStream::Init(nsACString& aContentType, nsCaptureParams* aParams)
|
||||
{
|
||||
if (!XRE_IsParentProcess())
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
mContentType = aContentType;
|
||||
mWidth = aParams->width;
|
||||
mHeight = aParams->height;
|
||||
mCamera = aParams->camera;
|
||||
|
||||
CameraStreamImpl *impl = CameraStreamImpl::GetInstance(0);
|
||||
if (!impl)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
if (impl->Init(mContentType, mCamera, mWidth, mHeight, this)) {
|
||||
mWidth = impl->GetWidth();
|
||||
mHeight = impl->GetHeight();
|
||||
mClosed = false;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void AndroidCameraInputStream::ReceiveFrame(char* frame, uint32_t length) {
|
||||
{
|
||||
mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
|
||||
if (mFrameQueue->GetSize() > MAX_FRAMES_QUEUED) {
|
||||
free(mFrameQueue->PopFront());
|
||||
mAvailable -= mFrameSize;
|
||||
}
|
||||
}
|
||||
|
||||
mFrameSize = sizeof(RawPacketHeader) + length;
|
||||
|
||||
char* fullFrame = (char*)moz_xmalloc(mFrameSize);
|
||||
|
||||
if (!fullFrame)
|
||||
return;
|
||||
|
||||
RawPacketHeader* header = (RawPacketHeader*) fullFrame;
|
||||
header->packetID = 0xFF;
|
||||
header->codecID = 0x595556; // "YUV"
|
||||
|
||||
// we copy the Y plane, and de-interlace the CrCb
|
||||
|
||||
uint32_t yFrameSize = mWidth * mHeight;
|
||||
uint32_t uvFrameSize = yFrameSize / 4;
|
||||
|
||||
memcpy(fullFrame + sizeof(RawPacketHeader), frame, yFrameSize);
|
||||
|
||||
char* uFrame = fullFrame + yFrameSize;
|
||||
char* vFrame = fullFrame + yFrameSize + uvFrameSize;
|
||||
char* yFrame = frame + yFrameSize;
|
||||
for (uint32_t i = 0; i < uvFrameSize; i++) {
|
||||
uFrame[i] = yFrame[2 * i + 1];
|
||||
vFrame[i] = yFrame[2 * i];
|
||||
}
|
||||
|
||||
{
|
||||
mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
|
||||
mAvailable += mFrameSize;
|
||||
mFrameQueue->Push((void*)fullFrame);
|
||||
}
|
||||
|
||||
NotifyListeners();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AndroidCameraInputStream::Available(uint64_t *aAvailable)
|
||||
{
|
||||
mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
|
||||
|
||||
*aAvailable = mAvailable;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP AndroidCameraInputStream::IsNonBlocking(bool *aNonBlock) {
|
||||
*aNonBlock = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP AndroidCameraInputStream::Read(char *aBuffer, uint32_t aCount, uint32_t *aRead) {
|
||||
return ReadSegments(NS_CopySegmentToBuffer, aBuffer, aCount, aRead);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP AndroidCameraInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, uint32_t aCount, uint32_t *aRead) {
|
||||
*aRead = 0;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
if (mAvailable == 0)
|
||||
return NS_BASE_STREAM_WOULD_BLOCK;
|
||||
|
||||
if (aCount > mAvailable)
|
||||
aCount = mAvailable;
|
||||
|
||||
if (!mHeaderSent) {
|
||||
CameraStreamImpl *impl = CameraStreamImpl::GetInstance(0);
|
||||
RawVideoHeader header;
|
||||
header.headerPacketID = 0;
|
||||
header.codecID = 0x595556; // "YUV"
|
||||
header.majorVersion = 0;
|
||||
header.minorVersion = 1;
|
||||
header.options = 1 | 1 << 1; // color, 4:2:2
|
||||
|
||||
header.alphaChannelBpp = 0;
|
||||
header.lumaChannelBpp = 8;
|
||||
header.chromaChannelBpp = 4;
|
||||
header.colorspace = 1;
|
||||
|
||||
header.frameWidth = mWidth;
|
||||
header.frameHeight = mHeight;
|
||||
header.aspectNumerator = 1;
|
||||
header.aspectDenominator = 1;
|
||||
|
||||
header.framerateNumerator = impl->GetFps();
|
||||
header.framerateDenominator = 1;
|
||||
|
||||
rv = aWriter(this, aClosure, (const char*)&header, 0, sizeof(RawVideoHeader), aRead);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return NS_OK;
|
||||
|
||||
mHeaderSent = true;
|
||||
aCount -= sizeof(RawVideoHeader);
|
||||
mAvailable -= sizeof(RawVideoHeader);
|
||||
}
|
||||
|
||||
{
|
||||
mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
|
||||
while ((mAvailable > 0) && (aCount >= mFrameSize)) {
|
||||
uint32_t readThisTime = 0;
|
||||
|
||||
char* frame = (char*)mFrameQueue->PopFront();
|
||||
rv = aWriter(this, aClosure, (const char*)frame, *aRead, mFrameSize, &readThisTime);
|
||||
|
||||
if (readThisTime != mFrameSize) {
|
||||
mFrameQueue->PushFront((void*)frame);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// RawReader does a copy when calling VideoData::Create()
|
||||
free(frame);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return NS_OK;
|
||||
|
||||
aCount -= readThisTime;
|
||||
mAvailable -= readThisTime;
|
||||
*aRead += readThisTime;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP AndroidCameraInputStream::Close() {
|
||||
return CloseWithStatus(NS_OK);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* must be called on the main (java) thread
|
||||
*/
|
||||
void AndroidCameraInputStream::doClose() {
|
||||
NS_ASSERTION(!mClosed, "Camera is already closed");
|
||||
|
||||
CameraStreamImpl *impl = CameraStreamImpl::GetInstance(0);
|
||||
impl->Close();
|
||||
mClosed = true;
|
||||
}
|
||||
|
||||
|
||||
void AndroidCameraInputStream::NotifyListeners() {
|
||||
mozilla::ReentrantMonitorAutoEnter autoMonitor(mMonitor);
|
||||
|
||||
if (mCallback && (mAvailable > sizeof(RawVideoHeader))) {
|
||||
nsCOMPtr<nsIInputStreamCallback> callback;
|
||||
if (mCallbackTarget) {
|
||||
callback = NS_NewInputStreamReadyEvent(mCallback, mCallbackTarget);
|
||||
} else {
|
||||
callback = mCallback;
|
||||
}
|
||||
|
||||
NS_ASSERTION(callback, "Shouldn't fail to make the callback!");
|
||||
|
||||
// Null the callback first because OnInputStreamReady may reenter AsyncWait
|
||||
mCallback = nullptr;
|
||||
mCallbackTarget = nullptr;
|
||||
|
||||
callback->OnInputStreamReady(this);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP AndroidCameraInputStream::AsyncWait(nsIInputStreamCallback *aCallback, uint32_t aFlags, uint32_t aRequestedCount, nsIEventTarget *aTarget)
|
||||
{
|
||||
if (aFlags != 0)
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
if (mCallback || mCallbackTarget)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
mCallbackTarget = aTarget;
|
||||
mCallback = aCallback;
|
||||
|
||||
// What we are being asked for may be present already
|
||||
NotifyListeners();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP AndroidCameraInputStream::CloseWithStatus(nsresult status)
|
||||
{
|
||||
AndroidCameraInputStream::doClose();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* AndroidCaptureProvider implementation
|
||||
*/
|
||||
|
||||
NS_IMPL_ISUPPORTS0(AndroidCaptureProvider)
|
||||
|
||||
AndroidCaptureProvider* AndroidCaptureProvider::sInstance = nullptr;
|
||||
|
||||
AndroidCaptureProvider::AndroidCaptureProvider() {
|
||||
}
|
||||
|
||||
AndroidCaptureProvider::~AndroidCaptureProvider() {
|
||||
AndroidCaptureProvider::sInstance = nullptr;
|
||||
}
|
||||
|
||||
nsresult AndroidCaptureProvider::Init(nsACString& aContentType,
|
||||
nsCaptureParams* aParams,
|
||||
nsIInputStream** aStream) {
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aParams);
|
||||
|
||||
NS_ASSERTION(aParams->frameLimit == 0 || aParams->timeLimit == 0,
|
||||
"Cannot set both a frame limit and a time limit!");
|
||||
|
||||
RefPtr<AndroidCameraInputStream> stream;
|
||||
|
||||
if (aContentType.EqualsLiteral("video/x-raw-yuv")) {
|
||||
stream = new AndroidCameraInputStream();
|
||||
if (stream) {
|
||||
nsresult rv = stream->Init(aContentType, aParams);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
}
|
||||
else
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
} else {
|
||||
NS_NOTREACHED("Should not have asked Android for this type!");
|
||||
}
|
||||
stream.forget(aStream);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<AndroidCaptureProvider> GetAndroidCaptureProvider() {
|
||||
if (!AndroidCaptureProvider::sInstance) {
|
||||
AndroidCaptureProvider::sInstance = new AndroidCaptureProvider();
|
||||
}
|
||||
RefPtr<AndroidCaptureProvider> ret = AndroidCaptureProvider::sInstance;
|
||||
return ret.forget();
|
||||
}
|
||||
68
netwerk/protocol/device/AndroidCaptureProvider.h
Normal file
68
netwerk/protocol/device/AndroidCaptureProvider.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 AndroidDeviceCaptureProvide_h_
|
||||
#define AndroidDeviceCaptureProvide_h_
|
||||
|
||||
#include "nsDeviceCaptureProvider.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/net/CameraStreamImpl.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsDeque.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
|
||||
class AndroidCaptureProvider final : public nsDeviceCaptureProvider {
|
||||
private:
|
||||
~AndroidCaptureProvider();
|
||||
|
||||
public:
|
||||
AndroidCaptureProvider();
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
MOZ_MUST_USE nsresult Init(nsACString& aContentType, nsCaptureParams* aParams, nsIInputStream** aStream) override;
|
||||
static AndroidCaptureProvider* sInstance;
|
||||
};
|
||||
|
||||
class AndroidCameraInputStream final : public nsIAsyncInputStream, mozilla::net::CameraStreamImpl::FrameCallback {
|
||||
private:
|
||||
~AndroidCameraInputStream();
|
||||
|
||||
public:
|
||||
AndroidCameraInputStream();
|
||||
|
||||
NS_IMETHODIMP Init(nsACString& aContentType, nsCaptureParams* aParams);
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
NS_DECL_NSIASYNCINPUTSTREAM
|
||||
|
||||
void ReceiveFrame(char* frame, uint32_t length) override;
|
||||
|
||||
protected:
|
||||
void NotifyListeners();
|
||||
void doClose();
|
||||
|
||||
uint32_t mAvailable;
|
||||
nsCString mContentType;
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
uint32_t mCamera;
|
||||
bool mHeaderSent;
|
||||
bool mClosed;
|
||||
nsDeque *mFrameQueue;
|
||||
uint32_t mFrameSize;
|
||||
mozilla::ReentrantMonitor mMonitor;
|
||||
|
||||
nsCOMPtr<nsIInputStreamCallback> mCallback;
|
||||
nsCOMPtr<nsIEventTarget> mCallbackTarget;
|
||||
};
|
||||
|
||||
already_AddRefed<AndroidCaptureProvider> GetAndroidCaptureProvider();
|
||||
|
||||
#endif
|
||||
114
netwerk/protocol/device/CameraStreamImpl.cpp
Normal file
114
netwerk/protocol/device/CameraStreamImpl.cpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/* 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/. */
|
||||
|
||||
#include "CameraStreamImpl.h"
|
||||
#include "GeneratedJNINatives.h"
|
||||
#include "nsCRTGlue.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
static CameraStreamImpl* mCamera0 = nullptr;
|
||||
static CameraStreamImpl* mCamera1 = nullptr;
|
||||
|
||||
class CameraStreamImpl::Callback
|
||||
: public java::GeckoAppShell::CameraCallback::Natives<Callback>
|
||||
{
|
||||
public:
|
||||
static void OnFrameData(int32_t aCamera, jni::ByteArray::Param aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
CameraStreamImpl* impl = GetInstance(uint32_t(aCamera));
|
||||
if (impl) {
|
||||
impl->TransmitFrame(aData);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* CameraStreamImpl
|
||||
*/
|
||||
|
||||
void CameraStreamImpl::TransmitFrame(jni::ByteArray::Param aData) {
|
||||
if (!mCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
JNIEnv* const env = jni::GetGeckoThreadEnv();
|
||||
const size_t length = size_t(env->GetArrayLength(aData.Get()));
|
||||
|
||||
if (!length) {
|
||||
return;
|
||||
}
|
||||
|
||||
jbyte* const data = env->GetByteArrayElements(aData.Get(), nullptr);
|
||||
mCallback->ReceiveFrame(reinterpret_cast<char*>(data), length);
|
||||
env->ReleaseByteArrayElements(aData.Get(), data, JNI_ABORT);
|
||||
}
|
||||
|
||||
CameraStreamImpl* CameraStreamImpl::GetInstance(uint32_t aCamera) {
|
||||
CameraStreamImpl* res = nullptr;
|
||||
switch(aCamera) {
|
||||
case 0:
|
||||
if (mCamera0)
|
||||
res = mCamera0;
|
||||
else
|
||||
res = mCamera0 = new CameraStreamImpl(aCamera);
|
||||
break;
|
||||
case 1:
|
||||
if (mCamera1)
|
||||
res = mCamera1;
|
||||
else
|
||||
res = mCamera1 = new CameraStreamImpl(aCamera);
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
CameraStreamImpl::CameraStreamImpl(uint32_t aCamera) :
|
||||
mInit(false), mCamera(aCamera)
|
||||
{
|
||||
NS_WARNING("CameraStreamImpl::CameraStreamImpl()");
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
mFps = 0;
|
||||
}
|
||||
|
||||
CameraStreamImpl::~CameraStreamImpl()
|
||||
{
|
||||
NS_WARNING("CameraStreamImpl::~CameraStreamImpl()");
|
||||
}
|
||||
|
||||
bool CameraStreamImpl::Init(const nsCString& contentType, const uint32_t& camera, const uint32_t& width, const uint32_t& height, FrameCallback* aCallback)
|
||||
{
|
||||
mCallback = aCallback;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
|
||||
Callback::Init();
|
||||
jni::IntArray::LocalRef retArray = java::GeckoAppShell::InitCamera(
|
||||
contentType, int32_t(camera), int32_t(width), int32_t(height));
|
||||
nsTArray<int32_t> ret = retArray->GetElements();
|
||||
|
||||
mWidth = uint32_t(ret[1]);
|
||||
mHeight = uint32_t(ret[2]);
|
||||
mFps = uint32_t(ret[3]);
|
||||
|
||||
return !!ret[0];
|
||||
}
|
||||
|
||||
void CameraStreamImpl::Close() {
|
||||
java::GeckoAppShell::CloseCamera();
|
||||
mCallback = nullptr;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
71
netwerk/protocol/device/CameraStreamImpl.h
Normal file
71
netwerk/protocol/device/CameraStreamImpl.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/* 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 __CAMERASTREAMIMPL_H__
|
||||
#define __CAMERASTREAMIMPL_H__
|
||||
|
||||
#include "mozilla/jni/Refs.h"
|
||||
|
||||
#include "nsString.h"
|
||||
|
||||
/**
|
||||
* This singleton class handles communication with the Android camera
|
||||
* through JNI. It is used by the IPDL parent or directly from the chrome process
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class CameraStreamImpl {
|
||||
public:
|
||||
class FrameCallback {
|
||||
public:
|
||||
virtual void ReceiveFrame(char* frame, uint32_t length) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* instance bound to a given camera
|
||||
*/
|
||||
static CameraStreamImpl* GetInstance(uint32_t aCamera);
|
||||
|
||||
bool initNeeded() {
|
||||
return !mInit;
|
||||
}
|
||||
|
||||
FrameCallback* GetFrameCallback() {
|
||||
return mCallback;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool Init(const nsCString& contentType, const uint32_t& camera, const uint32_t& width, const uint32_t& height, FrameCallback* callback);
|
||||
void Close();
|
||||
|
||||
uint32_t GetWidth() { return mWidth; }
|
||||
uint32_t GetHeight() { return mHeight; }
|
||||
uint32_t GetFps() { return mFps; }
|
||||
|
||||
void takePicture(const nsAString& aFileName);
|
||||
|
||||
private:
|
||||
class Callback;
|
||||
|
||||
CameraStreamImpl(uint32_t aCamera);
|
||||
CameraStreamImpl(const CameraStreamImpl&);
|
||||
CameraStreamImpl& operator=(const CameraStreamImpl&);
|
||||
|
||||
~CameraStreamImpl();
|
||||
|
||||
void TransmitFrame(jni::ByteArray::Param aData);
|
||||
|
||||
bool mInit;
|
||||
uint32_t mCamera;
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
uint32_t mFps;
|
||||
FrameCallback* mCallback;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
60
netwerk/protocol/device/RawStructs.h
Normal file
60
netwerk/protocol/device/RawStructs.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/* 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/. */
|
||||
|
||||
#if !defined(RawStructs_h_)
|
||||
#define RawStructs_h_
|
||||
|
||||
static const uint32_t RAW_ID = 0x595556;
|
||||
|
||||
struct nsRawVideo_PRUint24 {
|
||||
operator uint32_t() const { return value[2] << 16 | value[1] << 8 | value[0]; }
|
||||
nsRawVideo_PRUint24& operator= (const uint32_t& rhs)
|
||||
{ value[2] = (rhs & 0x00FF0000) >> 16;
|
||||
value[1] = (rhs & 0x0000FF00) >> 8;
|
||||
value[0] = (rhs & 0x000000FF);
|
||||
return *this; }
|
||||
private:
|
||||
uint8_t value[3];
|
||||
};
|
||||
|
||||
struct RawPacketHeader {
|
||||
typedef nsRawVideo_PRUint24 PRUint24;
|
||||
uint8_t packetID;
|
||||
PRUint24 codecID;
|
||||
};
|
||||
|
||||
// This is Arc's draft from wiki.xiph.org/OggYUV
|
||||
struct RawVideoHeader {
|
||||
typedef nsRawVideo_PRUint24 PRUint24;
|
||||
uint8_t headerPacketID; // Header Packet ID (always 0)
|
||||
PRUint24 codecID; // Codec identifier (always "YUV")
|
||||
uint8_t majorVersion; // Version Major (breaks backwards compat)
|
||||
uint8_t minorVersion; // Version Minor (preserves backwards compat)
|
||||
uint16_t options; // Bit 1: Color (false = B/W)
|
||||
// Bits 2-4: Chroma Pixel Shape
|
||||
// Bit 5: 50% horizontal offset for Cr samples
|
||||
// Bit 6: 50% vertical ...
|
||||
// Bits 7-8: Chroma Blending
|
||||
// Bit 9: Packed (false = Planar)
|
||||
// Bit 10: Cr Staggered Horizontally
|
||||
// Bit 11: Cr Staggered Vertically
|
||||
// Bit 12: Unused (format is always little endian)
|
||||
// Bit 13: Interlaced (false = Progressive)
|
||||
// Bits 14-16: Interlace options (undefined)
|
||||
|
||||
uint8_t alphaChannelBpp;
|
||||
uint8_t lumaChannelBpp;
|
||||
uint8_t chromaChannelBpp;
|
||||
uint8_t colorspace;
|
||||
|
||||
PRUint24 frameWidth;
|
||||
PRUint24 frameHeight;
|
||||
PRUint24 aspectNumerator;
|
||||
PRUint24 aspectDenominator;
|
||||
|
||||
uint32_t framerateNumerator;
|
||||
uint32_t framerateDenominator;
|
||||
};
|
||||
|
||||
#endif // RawStructs_h_
|
||||
27
netwerk/protocol/device/moz.build
Normal file
27
netwerk/protocol/device/moz.build
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
|
||||
EXPORTS.mozilla.net += [
|
||||
'CameraStreamImpl.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'AndroidCaptureProvider.cpp',
|
||||
'CameraStreamImpl.cpp',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'nsDeviceChannel.cpp',
|
||||
'nsDeviceProtocolHandler.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/netwerk/base/',
|
||||
]
|
||||
31
netwerk/protocol/device/nsDeviceCaptureProvider.h
Normal file
31
netwerk/protocol/device/nsDeviceCaptureProvider.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 nsDeviceCaptureProvider_h_
|
||||
#define nsDeviceCaptureProvider_h_
|
||||
|
||||
#include "nsIInputStream.h"
|
||||
|
||||
struct nsCaptureParams {
|
||||
bool captureAudio;
|
||||
bool captureVideo;
|
||||
uint32_t frameRate;
|
||||
uint32_t frameLimit;
|
||||
uint32_t timeLimit;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t bpp;
|
||||
uint32_t camera;
|
||||
};
|
||||
|
||||
class nsDeviceCaptureProvider : public nsISupports
|
||||
{
|
||||
public:
|
||||
virtual MOZ_MUST_USE nsresult Init(nsACString& aContentType,
|
||||
nsCaptureParams* aParams,
|
||||
nsIInputStream** aStream) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
154
netwerk/protocol/device/nsDeviceChannel.cpp
Normal file
154
netwerk/protocol/device/nsDeviceChannel.cpp
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "plstr.h"
|
||||
#include "nsDeviceChannel.h"
|
||||
#include "nsDeviceCaptureProvider.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "AndroidCaptureProvider.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
// Copied from image/decoders/icon/nsIconURI.cpp
|
||||
// takes a string like ?size=32&contentType=text/html and returns a new string
|
||||
// containing just the attribute values. i.e you could pass in this string with
|
||||
// an attribute name of "size=", this will return 32
|
||||
// Assumption: attribute pairs are separated by &
|
||||
void extractAttributeValue(const char* searchString, const char* attributeName, nsCString& result)
|
||||
{
|
||||
result.Truncate();
|
||||
|
||||
if (!searchString || !attributeName)
|
||||
return;
|
||||
|
||||
uint32_t attributeNameSize = strlen(attributeName);
|
||||
const char *startOfAttribute = PL_strcasestr(searchString, attributeName);
|
||||
if (!startOfAttribute ||
|
||||
!( *(startOfAttribute-1) == '?' || *(startOfAttribute-1) == '&') )
|
||||
return;
|
||||
|
||||
startOfAttribute += attributeNameSize; // Skip the attributeName
|
||||
if (!*startOfAttribute)
|
||||
return;
|
||||
|
||||
const char *endOfAttribute = strchr(startOfAttribute, '&');
|
||||
if (endOfAttribute) {
|
||||
result.Assign(Substring(startOfAttribute, endOfAttribute));
|
||||
} else {
|
||||
result.Assign(startOfAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(nsDeviceChannel,
|
||||
nsBaseChannel,
|
||||
nsIChannel)
|
||||
|
||||
// nsDeviceChannel methods
|
||||
nsDeviceChannel::nsDeviceChannel()
|
||||
{
|
||||
SetContentType(NS_LITERAL_CSTRING("image/png"));
|
||||
}
|
||||
|
||||
nsDeviceChannel::~nsDeviceChannel()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDeviceChannel::Init(nsIURI* aUri)
|
||||
{
|
||||
nsBaseChannel::Init();
|
||||
nsBaseChannel::SetURI(aUri);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDeviceChannel::OpenContentStream(bool aAsync,
|
||||
nsIInputStream** aStream,
|
||||
nsIChannel** aChannel)
|
||||
{
|
||||
if (!aAsync)
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
nsCOMPtr<nsIURI> uri = nsBaseChannel::URI();
|
||||
*aStream = nullptr;
|
||||
*aChannel = nullptr;
|
||||
NS_NAMED_LITERAL_CSTRING(width, "width=");
|
||||
NS_NAMED_LITERAL_CSTRING(height, "height=");
|
||||
|
||||
nsAutoCString spec;
|
||||
nsresult rv = uri->GetSpec(spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString type;
|
||||
|
||||
RefPtr<nsDeviceCaptureProvider> capture;
|
||||
nsCaptureParams captureParams;
|
||||
captureParams.camera = 0;
|
||||
if (kNotFound != spec.Find(NS_LITERAL_CSTRING("type=image/png"),
|
||||
true,
|
||||
0,
|
||||
-1)) {
|
||||
type.AssignLiteral("image/png");
|
||||
SetContentType(type);
|
||||
captureParams.captureAudio = false;
|
||||
captureParams.captureVideo = true;
|
||||
captureParams.timeLimit = 0;
|
||||
captureParams.frameLimit = 1;
|
||||
nsAutoCString buffer;
|
||||
extractAttributeValue(spec.get(), "width=", buffer);
|
||||
nsresult err;
|
||||
captureParams.width = buffer.ToInteger(&err);
|
||||
if (!captureParams.width)
|
||||
captureParams.width = 640;
|
||||
extractAttributeValue(spec.get(), "height=", buffer);
|
||||
captureParams.height = buffer.ToInteger(&err);
|
||||
if (!captureParams.height)
|
||||
captureParams.height = 480;
|
||||
extractAttributeValue(spec.get(), "camera=", buffer);
|
||||
captureParams.camera = buffer.ToInteger(&err);
|
||||
captureParams.bpp = 32;
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
capture = GetAndroidCaptureProvider();
|
||||
#endif
|
||||
} else if (kNotFound != spec.Find(NS_LITERAL_CSTRING("type=video/x-raw-yuv"),
|
||||
true,
|
||||
0,
|
||||
-1)) {
|
||||
type.AssignLiteral("video/x-raw-yuv");
|
||||
SetContentType(type);
|
||||
captureParams.captureAudio = false;
|
||||
captureParams.captureVideo = true;
|
||||
nsAutoCString buffer;
|
||||
extractAttributeValue(spec.get(), "width=", buffer);
|
||||
nsresult err;
|
||||
captureParams.width = buffer.ToInteger(&err);
|
||||
if (!captureParams.width)
|
||||
captureParams.width = 640;
|
||||
extractAttributeValue(spec.get(), "height=", buffer);
|
||||
captureParams.height = buffer.ToInteger(&err);
|
||||
if (!captureParams.height)
|
||||
captureParams.height = 480;
|
||||
extractAttributeValue(spec.get(), "camera=", buffer);
|
||||
captureParams.camera = buffer.ToInteger(&err);
|
||||
captureParams.bpp = 32;
|
||||
captureParams.timeLimit = 0;
|
||||
captureParams.frameLimit = 60000;
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// only enable if "device.camera.enabled" is true.
|
||||
if (Preferences::GetBool("device.camera.enabled", false) == true)
|
||||
capture = GetAndroidCaptureProvider();
|
||||
#endif
|
||||
} else {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
if (!capture)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return capture->Init(type, &captureParams, aStream);
|
||||
}
|
||||
26
netwerk/protocol/device/nsDeviceChannel.h
Normal file
26
netwerk/protocol/device/nsDeviceChannel.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 nsDeviceChannel_h_
|
||||
#define nsDeviceChannel_h_
|
||||
|
||||
#include "nsBaseChannel.h"
|
||||
|
||||
class nsDeviceChannel : public nsBaseChannel
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
nsDeviceChannel();
|
||||
|
||||
MOZ_MUST_USE nsresult Init(nsIURI* uri);
|
||||
MOZ_MUST_USE nsresult OpenContentStream(bool aAsync,
|
||||
nsIInputStream **aStream,
|
||||
nsIChannel **aChannel) override;
|
||||
|
||||
protected:
|
||||
~nsDeviceChannel();
|
||||
};
|
||||
#endif
|
||||
93
netwerk/protocol/device/nsDeviceProtocolHandler.cpp
Normal file
93
netwerk/protocol/device/nsDeviceProtocolHandler.cpp
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsDeviceProtocolHandler.h"
|
||||
#include "nsDeviceChannel.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsSimpleURI.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
NS_IMPL_ISUPPORTS(nsDeviceProtocolHandler,
|
||||
nsIProtocolHandler)
|
||||
|
||||
nsresult
|
||||
nsDeviceProtocolHandler::Init(){
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDeviceProtocolHandler::GetScheme(nsACString &aResult)
|
||||
{
|
||||
aResult.AssignLiteral("moz-device");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDeviceProtocolHandler::GetDefaultPort(int32_t *aResult)
|
||||
{
|
||||
*aResult = -1; // no port for moz_device: URLs
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDeviceProtocolHandler::GetProtocolFlags(uint32_t *aResult)
|
||||
{
|
||||
*aResult = URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDeviceProtocolHandler::NewURI(const nsACString &spec,
|
||||
const char *originCharset,
|
||||
nsIURI *baseURI,
|
||||
nsIURI **result)
|
||||
{
|
||||
RefPtr<nsSimpleURI> uri = new nsSimpleURI();
|
||||
|
||||
nsresult rv = uri->SetSpec(spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uri.forget(result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDeviceProtocolHandler::NewChannel2(nsIURI* aURI,
|
||||
nsILoadInfo* aLoadInfo,
|
||||
nsIChannel** aResult)
|
||||
{
|
||||
RefPtr<nsDeviceChannel> channel = new nsDeviceChannel();
|
||||
nsresult rv = channel->Init(aURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// set the loadInfo on the new channel
|
||||
rv = channel->SetLoadInfo(aLoadInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
channel.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDeviceProtocolHandler::NewChannel(nsIURI* aURI, nsIChannel **aResult)
|
||||
{
|
||||
return NewChannel2(aURI, nullptr, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDeviceProtocolHandler::AllowPort(int32_t port,
|
||||
const char *scheme,
|
||||
bool *aResult)
|
||||
{
|
||||
// don't override anything.
|
||||
*aResult = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
34
netwerk/protocol/device/nsDeviceProtocolHandler.h
Normal file
34
netwerk/protocol/device/nsDeviceProtocolHandler.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 nsDeviceProtocolHandler_h_
|
||||
#define nsDeviceProtocolHandler_h_
|
||||
|
||||
#include "nsIProtocolHandler.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
// {6b0ffe9e-d114-486b-aeb7-da62e7273ed5}
|
||||
#define NS_DEVICEPROTOCOLHANDLER_CID \
|
||||
{ 0x60ffe9e, 0xd114, 0x486b, \
|
||||
{0xae, 0xb7, 0xda, 0x62, 0xe7, 0x27, 0x3e, 0xd5} }
|
||||
|
||||
class nsDeviceProtocolHandler final : public nsIProtocolHandler {
|
||||
~nsDeviceProtocolHandler() {}
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIPROTOCOLHANDLER
|
||||
|
||||
nsDeviceProtocolHandler() {}
|
||||
|
||||
MOZ_MUST_USE nsresult Init();
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
#endif
|
||||
30
netwerk/protocol/file/moz.build
Normal file
30
netwerk/protocol/file/moz.build
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
EXPORTS.mozilla.net += [
|
||||
'nsFileProtocolHandler.h',
|
||||
]
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIFileChannel.idl',
|
||||
'nsIFileProtocolHandler.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'necko_file'
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'nsFileChannel.cpp',
|
||||
'nsFileProtocolHandler.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/netwerk/base',
|
||||
]
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
486
netwerk/protocol/file/nsFileChannel.cpp
Normal file
486
netwerk/protocol/file/nsFileChannel.cpp
Normal file
|
|
@ -0,0 +1,486 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cin: */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsIOService.h"
|
||||
#include "nsFileChannel.h"
|
||||
#include "nsBaseContentStream.h"
|
||||
#include "nsDirectoryIndexStream.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsTransportUtils.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsMimeTypes.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsFileProtocolHandler.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
#include "nsIFileURL.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIMIMEService.h"
|
||||
#include "prio.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::net;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsFileCopyEvent : public Runnable {
|
||||
public:
|
||||
nsFileCopyEvent(nsIOutputStream *dest, nsIInputStream *source, int64_t len)
|
||||
: mDest(dest)
|
||||
, mSource(source)
|
||||
, mLen(len)
|
||||
, mStatus(NS_OK)
|
||||
, mInterruptStatus(NS_OK) {
|
||||
}
|
||||
|
||||
// Read the current status of the file copy operation.
|
||||
nsresult Status() { return mStatus; }
|
||||
|
||||
// Call this method to perform the file copy synchronously.
|
||||
void DoCopy();
|
||||
|
||||
// Call this method to perform the file copy on a background thread. The
|
||||
// callback is dispatched when the file copy completes.
|
||||
nsresult Dispatch(nsIRunnable *callback,
|
||||
nsITransportEventSink *sink,
|
||||
nsIEventTarget *target);
|
||||
|
||||
// Call this method to interrupt a file copy operation that is occuring on
|
||||
// a background thread. The status parameter passed to this function must
|
||||
// be a failure code and is set as the status of this file copy operation.
|
||||
void Interrupt(nsresult status) {
|
||||
NS_ASSERTION(NS_FAILED(status), "must be a failure code");
|
||||
mInterruptStatus = status;
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override {
|
||||
DoCopy();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIEventTarget> mCallbackTarget;
|
||||
nsCOMPtr<nsIRunnable> mCallback;
|
||||
nsCOMPtr<nsITransportEventSink> mSink;
|
||||
nsCOMPtr<nsIOutputStream> mDest;
|
||||
nsCOMPtr<nsIInputStream> mSource;
|
||||
int64_t mLen;
|
||||
nsresult mStatus; // modified on i/o thread only
|
||||
nsresult mInterruptStatus; // modified on main thread only
|
||||
};
|
||||
|
||||
void
|
||||
nsFileCopyEvent::DoCopy()
|
||||
{
|
||||
// We'll copy in chunks this large by default. This size affects how
|
||||
// frequently we'll check for interrupts.
|
||||
const int32_t chunk = nsIOService::gDefaultSegmentSize * nsIOService::gDefaultSegmentCount;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
int64_t len = mLen, progress = 0;
|
||||
while (len) {
|
||||
// If we've been interrupted, then stop copying.
|
||||
rv = mInterruptStatus;
|
||||
if (NS_FAILED(rv))
|
||||
break;
|
||||
|
||||
int32_t num = std::min((int32_t) len, chunk);
|
||||
|
||||
uint32_t result;
|
||||
rv = mSource->ReadSegments(NS_CopySegmentToStream, mDest, num, &result);
|
||||
if (NS_FAILED(rv))
|
||||
break;
|
||||
if (result != (uint32_t) num) {
|
||||
rv = NS_ERROR_FILE_DISK_FULL; // stopped prematurely (out of disk space)
|
||||
break;
|
||||
}
|
||||
|
||||
// Dispatch progress notification
|
||||
if (mSink) {
|
||||
progress += num;
|
||||
mSink->OnTransportStatus(nullptr, NS_NET_STATUS_WRITING, progress,
|
||||
mLen);
|
||||
}
|
||||
|
||||
len -= num;
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
mStatus = rv;
|
||||
|
||||
// Close the output stream before notifying our callback so that others may
|
||||
// freely "play" with the file.
|
||||
mDest->Close();
|
||||
|
||||
// Notify completion
|
||||
if (mCallback) {
|
||||
mCallbackTarget->Dispatch(mCallback, NS_DISPATCH_NORMAL);
|
||||
|
||||
// Release the callback on the target thread to avoid destroying stuff on
|
||||
// the wrong thread.
|
||||
NS_ProxyRelease(mCallbackTarget, mCallback.forget());
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFileCopyEvent::Dispatch(nsIRunnable *callback,
|
||||
nsITransportEventSink *sink,
|
||||
nsIEventTarget *target)
|
||||
{
|
||||
// Use the supplied event target for all asynchronous operations.
|
||||
|
||||
mCallback = callback;
|
||||
mCallbackTarget = target;
|
||||
|
||||
// Build a coalescing proxy for progress events
|
||||
nsresult rv = net_NewTransportEventSinkProxy(getter_AddRefs(mSink), sink, target);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// Dispatch ourselves to I/O thread pool...
|
||||
nsCOMPtr<nsIEventTarget> pool =
|
||||
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
return pool->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// This is a dummy input stream that when read, performs the file copy. The
|
||||
// copy happens on a background thread via mCopyEvent.
|
||||
|
||||
class nsFileUploadContentStream : public nsBaseContentStream {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
nsFileUploadContentStream(bool nonBlocking,
|
||||
nsIOutputStream *dest,
|
||||
nsIInputStream *source,
|
||||
int64_t len,
|
||||
nsITransportEventSink *sink)
|
||||
: nsBaseContentStream(nonBlocking)
|
||||
, mCopyEvent(new nsFileCopyEvent(dest, source, len))
|
||||
, mSink(sink) {
|
||||
}
|
||||
|
||||
bool IsInitialized() {
|
||||
return mCopyEvent != nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHOD ReadSegments(nsWriteSegmentFun fun, void *closure,
|
||||
uint32_t count, uint32_t *result) override;
|
||||
NS_IMETHOD AsyncWait(nsIInputStreamCallback *callback, uint32_t flags,
|
||||
uint32_t count, nsIEventTarget *target) override;
|
||||
|
||||
private:
|
||||
virtual ~nsFileUploadContentStream() {}
|
||||
|
||||
void OnCopyComplete();
|
||||
|
||||
RefPtr<nsFileCopyEvent> mCopyEvent;
|
||||
nsCOMPtr<nsITransportEventSink> mSink;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(nsFileUploadContentStream,
|
||||
nsBaseContentStream)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileUploadContentStream::ReadSegments(nsWriteSegmentFun fun, void *closure,
|
||||
uint32_t count, uint32_t *result)
|
||||
{
|
||||
*result = 0; // nothing is ever actually read from this stream
|
||||
|
||||
if (IsClosed())
|
||||
return NS_OK;
|
||||
|
||||
if (IsNonBlocking()) {
|
||||
// Inform the caller that they will have to wait for the copy operation to
|
||||
// complete asynchronously. We'll kick of the copy operation once they
|
||||
// call AsyncWait.
|
||||
return NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
|
||||
// Perform copy synchronously, and then close out the stream.
|
||||
mCopyEvent->DoCopy();
|
||||
nsresult status = mCopyEvent->Status();
|
||||
CloseWithStatus(NS_FAILED(status) ? status : NS_BASE_STREAM_CLOSED);
|
||||
return status;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileUploadContentStream::AsyncWait(nsIInputStreamCallback *callback,
|
||||
uint32_t flags, uint32_t count,
|
||||
nsIEventTarget *target)
|
||||
{
|
||||
nsresult rv = nsBaseContentStream::AsyncWait(callback, flags, count, target);
|
||||
if (NS_FAILED(rv) || IsClosed())
|
||||
return rv;
|
||||
|
||||
if (IsNonBlocking()) {
|
||||
nsCOMPtr<nsIRunnable> callback =
|
||||
NewRunnableMethod(this, &nsFileUploadContentStream::OnCopyComplete);
|
||||
mCopyEvent->Dispatch(callback, mSink, target);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsFileUploadContentStream::OnCopyComplete()
|
||||
{
|
||||
// This method is being called to indicate that we are done copying.
|
||||
nsresult status = mCopyEvent->Status();
|
||||
|
||||
CloseWithStatus(NS_FAILED(status) ? status : NS_BASE_STREAM_CLOSED);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsFileChannel::nsFileChannel(nsIURI *uri)
|
||||
{
|
||||
// If we have a link file, we should resolve its target right away.
|
||||
// This is to protect against a same origin attack where the same link file
|
||||
// can point to different resources right after the first resource is loaded.
|
||||
nsCOMPtr<nsIFile> file;
|
||||
nsCOMPtr <nsIURI> targetURI;
|
||||
nsAutoCString fileTarget;
|
||||
nsCOMPtr<nsIFile> resolvedFile;
|
||||
bool symLink;
|
||||
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(uri);
|
||||
if (fileURL &&
|
||||
NS_SUCCEEDED(fileURL->GetFile(getter_AddRefs(file))) &&
|
||||
NS_SUCCEEDED(file->IsSymlink(&symLink)) &&
|
||||
symLink &&
|
||||
NS_SUCCEEDED(file->GetNativeTarget(fileTarget)) &&
|
||||
NS_SUCCEEDED(NS_NewNativeLocalFile(fileTarget, PR_TRUE,
|
||||
getter_AddRefs(resolvedFile))) &&
|
||||
NS_SUCCEEDED(NS_NewFileURI(getter_AddRefs(targetURI),
|
||||
resolvedFile, nullptr))) {
|
||||
// Make an effort to match up the query strings.
|
||||
nsCOMPtr<nsIURL> origURL = do_QueryInterface(uri);
|
||||
nsCOMPtr<nsIURL> targetURL = do_QueryInterface(targetURI);
|
||||
nsAutoCString queryString;
|
||||
if (origURL && targetURL && NS_SUCCEEDED(origURL->GetQuery(queryString))) {
|
||||
targetURL->SetQuery(queryString);
|
||||
}
|
||||
|
||||
SetURI(targetURI);
|
||||
SetOriginalURI(uri);
|
||||
nsLoadFlags loadFlags = 0;
|
||||
GetLoadFlags(&loadFlags);
|
||||
SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
|
||||
} else {
|
||||
SetURI(uri);
|
||||
}
|
||||
}
|
||||
|
||||
nsFileChannel::~nsFileChannel()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFileChannel::MakeFileInputStream(nsIFile *file,
|
||||
nsCOMPtr<nsIInputStream> &stream,
|
||||
nsCString &contentType,
|
||||
bool async)
|
||||
{
|
||||
// we accept that this might result in a disk hit to stat the file
|
||||
bool isDir;
|
||||
nsresult rv = file->IsDirectory(&isDir);
|
||||
if (NS_FAILED(rv)) {
|
||||
// canonicalize error message
|
||||
if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
|
||||
rv = NS_ERROR_FILE_NOT_FOUND;
|
||||
|
||||
if (async && (NS_ERROR_FILE_NOT_FOUND == rv)) {
|
||||
// We don't return "Not Found" errors here. Since we could not find
|
||||
// the file, it's not a directory anyway.
|
||||
isDir = false;
|
||||
} else {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDir) {
|
||||
rv = nsDirectoryIndexStream::Create(file, getter_AddRefs(stream));
|
||||
if (NS_SUCCEEDED(rv) && !HasContentTypeHint())
|
||||
contentType.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT);
|
||||
} else {
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file, -1, -1,
|
||||
async? nsIFileInputStream::DEFER_OPEN : 0);
|
||||
if (NS_SUCCEEDED(rv) && !HasContentTypeHint()) {
|
||||
// Use file extension to infer content type
|
||||
nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1", &rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mime->GetTypeFromFile(file, contentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFileChannel::OpenContentStream(bool async, nsIInputStream **result,
|
||||
nsIChannel** channel)
|
||||
{
|
||||
// NOTE: the resulting file is a clone, so it is safe to pass it to the
|
||||
// file input stream which will be read on a background thread.
|
||||
nsCOMPtr<nsIFile> file;
|
||||
nsresult rv = GetFile(getter_AddRefs(file));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsIFileProtocolHandler> fileHandler;
|
||||
rv = NS_GetFileProtocolHandler(getter_AddRefs(fileHandler));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsIURI> newURI;
|
||||
rv = fileHandler->ReadURLFile(file, getter_AddRefs(newURI));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsCOMPtr<nsIChannel> newChannel;
|
||||
rv = NS_NewChannel(getter_AddRefs(newChannel),
|
||||
newURI,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_OTHER);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
*result = nullptr;
|
||||
newChannel.forget(channel);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
|
||||
if (mUploadStream) {
|
||||
// Pass back a nsFileUploadContentStream instance that knows how to perform
|
||||
// the file copy when "read" (the resulting stream in this case does not
|
||||
// actually return any data).
|
||||
|
||||
nsCOMPtr<nsIOutputStream> fileStream;
|
||||
rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileStream), file,
|
||||
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
|
||||
PR_IRUSR | PR_IWUSR);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
RefPtr<nsFileUploadContentStream> uploadStream =
|
||||
new nsFileUploadContentStream(async, fileStream, mUploadStream,
|
||||
mUploadLength, this);
|
||||
if (!uploadStream || !uploadStream->IsInitialized()) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
stream = uploadStream.forget();
|
||||
|
||||
mContentLength = 0;
|
||||
|
||||
// Since there isn't any content to speak of we just set the content-type
|
||||
// to something other than "unknown" to avoid triggering the content-type
|
||||
// sniffer code in nsBaseChannel.
|
||||
// However, don't override explicitly set types.
|
||||
if (!HasContentTypeHint())
|
||||
SetContentType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM));
|
||||
} else {
|
||||
nsAutoCString contentType;
|
||||
rv = MakeFileInputStream(file, stream, contentType, async);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
EnableSynthesizedProgressEvents(true);
|
||||
|
||||
// fixup content length and type
|
||||
if (mContentLength < 0) {
|
||||
int64_t size;
|
||||
rv = file->GetFileSize(&size);
|
||||
if (NS_FAILED(rv)) {
|
||||
if (async &&
|
||||
(NS_ERROR_FILE_NOT_FOUND == rv ||
|
||||
NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv)) {
|
||||
size = 0;
|
||||
} else {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
mContentLength = size;
|
||||
}
|
||||
if (!contentType.IsEmpty())
|
||||
SetContentType(contentType);
|
||||
}
|
||||
|
||||
*result = nullptr;
|
||||
stream.swap(*result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsFileChannel::nsISupports
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(nsFileChannel,
|
||||
nsBaseChannel,
|
||||
nsIUploadChannel,
|
||||
nsIFileChannel)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsFileChannel::nsIFileChannel
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileChannel::GetFile(nsIFile **file)
|
||||
{
|
||||
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(URI());
|
||||
NS_ENSURE_STATE(fileURL);
|
||||
|
||||
// This returns a cloned nsIFile
|
||||
return fileURL->GetFile(file);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsFileChannel::nsIUploadChannel
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileChannel::SetUploadStream(nsIInputStream *stream,
|
||||
const nsACString &contentType,
|
||||
int64_t contentLength)
|
||||
{
|
||||
NS_ENSURE_TRUE(!Pending(), NS_ERROR_IN_PROGRESS);
|
||||
|
||||
if ((mUploadStream = stream)) {
|
||||
mUploadLength = contentLength;
|
||||
if (mUploadLength < 0) {
|
||||
// Make sure we know how much data we are uploading.
|
||||
uint64_t avail;
|
||||
nsresult rv = mUploadStream->Available(&avail);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
// if this doesn't fit in the javascript MAX_SAFE_INTEGER
|
||||
// pretend we don't know the size
|
||||
mUploadLength = InScriptableRange(avail) ? avail : -1;
|
||||
}
|
||||
} else {
|
||||
mUploadLength = -1;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileChannel::GetUploadStream(nsIInputStream **result)
|
||||
{
|
||||
NS_IF_ADDREF(*result = mUploadStream);
|
||||
return NS_OK;
|
||||
}
|
||||
45
netwerk/protocol/file/nsFileChannel.h
Normal file
45
netwerk/protocol/file/nsFileChannel.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cin: */
|
||||
/* 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 nsFileChannel_h__
|
||||
#define nsFileChannel_h__
|
||||
|
||||
#include "nsBaseChannel.h"
|
||||
#include "nsIFileChannel.h"
|
||||
#include "nsIUploadChannel.h"
|
||||
|
||||
class nsFileChannel : public nsBaseChannel
|
||||
, public nsIFileChannel
|
||||
, public nsIUploadChannel
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIFILECHANNEL
|
||||
NS_DECL_NSIUPLOADCHANNEL
|
||||
|
||||
explicit nsFileChannel(nsIURI *uri);
|
||||
|
||||
protected:
|
||||
~nsFileChannel();
|
||||
|
||||
// Called to construct a blocking file input stream for the given file. This
|
||||
// method also returns a best guess at the content-type for the data stream.
|
||||
// NOTE: If the channel has a type hint set, contentType will be left
|
||||
// untouched. The caller should not use it in that case.
|
||||
MOZ_MUST_USE nsresult MakeFileInputStream(nsIFile *file,
|
||||
nsCOMPtr<nsIInputStream> &stream,
|
||||
nsCString &contentType, bool async);
|
||||
|
||||
virtual MOZ_MUST_USE nsresult OpenContentStream(bool async,
|
||||
nsIInputStream **result,
|
||||
nsIChannel** channel) override;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIInputStream> mUploadStream;
|
||||
int64_t mUploadLength;
|
||||
};
|
||||
|
||||
#endif // !nsFileChannel_h__
|
||||
274
netwerk/protocol/file/nsFileProtocolHandler.cpp
Normal file
274
netwerk/protocol/file/nsFileProtocolHandler.cpp
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
// vim:ts=4 sw=4 sts=4 et cin:
|
||||
/* 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/. */
|
||||
|
||||
#include "nsIFile.h"
|
||||
#include "nsFileProtocolHandler.h"
|
||||
#include "nsFileChannel.h"
|
||||
#include "nsStandardURL.h"
|
||||
#include "nsURLHelper.h"
|
||||
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
// URL file handling, copied and modified from xpfe/components/bookmarks/src/nsBookmarksService.cpp
|
||||
#ifdef XP_WIN
|
||||
#include <shlobj.h>
|
||||
#include <intshcut.h>
|
||||
#include "nsIFileURL.h"
|
||||
#ifdef CompareString
|
||||
#undef CompareString
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// URL file handling for freedesktop.org
|
||||
#ifdef XP_UNIX
|
||||
#include "nsINIParser.h"
|
||||
#define DESKTOP_ENTRY_SECTION "Desktop Entry"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsFileProtocolHandler::nsFileProtocolHandler()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFileProtocolHandler::Init()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsFileProtocolHandler,
|
||||
nsIFileProtocolHandler,
|
||||
nsIProtocolHandler,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIProtocolHandler methods:
|
||||
|
||||
#if defined(XP_WIN)
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::ReadURLFile(nsIFile* aFile, nsIURI** aURI)
|
||||
{
|
||||
nsAutoString path;
|
||||
nsresult rv = aFile->GetPath(path);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (path.Length() < 4)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
if (!StringTail(path, 4).LowerCaseEqualsLiteral(".url"))
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
HRESULT result;
|
||||
|
||||
rv = NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
IUniformResourceLocatorW* urlLink = nullptr;
|
||||
result = ::CoCreateInstance(CLSID_InternetShortcut, nullptr, CLSCTX_INPROC_SERVER,
|
||||
IID_IUniformResourceLocatorW, (void**)&urlLink);
|
||||
if (SUCCEEDED(result) && urlLink) {
|
||||
IPersistFile* urlFile = nullptr;
|
||||
result = urlLink->QueryInterface(IID_IPersistFile, (void**)&urlFile);
|
||||
if (SUCCEEDED(result) && urlFile) {
|
||||
result = urlFile->Load(path.get(), STGM_READ);
|
||||
if (SUCCEEDED(result) ) {
|
||||
LPWSTR lpTemp = nullptr;
|
||||
|
||||
// The URL this method will give us back seems to be already
|
||||
// escaped. Hence, do not do escaping of our own.
|
||||
result = urlLink->GetURL(&lpTemp);
|
||||
if (SUCCEEDED(result) && lpTemp) {
|
||||
rv = NS_NewURI(aURI, nsDependentString(lpTemp));
|
||||
// free the string that GetURL alloc'd
|
||||
CoTaskMemFree(lpTemp);
|
||||
}
|
||||
}
|
||||
urlFile->Release();
|
||||
}
|
||||
urlLink->Release();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
#elif defined(XP_UNIX)
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::ReadURLFile(nsIFile* aFile, nsIURI** aURI)
|
||||
{
|
||||
// We only support desktop files that end in ".desktop" like the spec says:
|
||||
// http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s02.html
|
||||
nsAutoCString leafName;
|
||||
nsresult rv = aFile->GetNativeLeafName(leafName);
|
||||
if (NS_FAILED(rv) ||
|
||||
!StringEndsWith(leafName, NS_LITERAL_CSTRING(".desktop")))
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
bool isFile = false;
|
||||
rv = aFile->IsFile(&isFile);
|
||||
if (NS_FAILED(rv) || !isFile) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsINIParser parser;
|
||||
rv = parser.Init(aFile);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsAutoCString type;
|
||||
parser.GetString(DESKTOP_ENTRY_SECTION, "Type", type);
|
||||
if (!type.EqualsLiteral("Link"))
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
nsAutoCString url;
|
||||
rv = parser.GetString(DESKTOP_ENTRY_SECTION, "URL", url);
|
||||
if (NS_FAILED(rv) || url.IsEmpty())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
return NS_NewURI(aURI, url);
|
||||
}
|
||||
|
||||
#else // other platforms
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::ReadURLFile(nsIFile* aFile, nsIURI** aURI)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
#endif // ReadURLFile()
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::GetScheme(nsACString &result)
|
||||
{
|
||||
result.AssignLiteral("file");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::GetDefaultPort(int32_t *result)
|
||||
{
|
||||
*result = -1; // no port for file: URLs
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::GetProtocolFlags(uint32_t *result)
|
||||
{
|
||||
*result = URI_NOAUTH | URI_IS_LOCAL_FILE | URI_IS_LOCAL_RESOURCE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::NewURI(const nsACString &spec,
|
||||
const char *charset,
|
||||
nsIURI *baseURI,
|
||||
nsIURI **result)
|
||||
{
|
||||
nsCOMPtr<nsIStandardURL> url = new nsStandardURL(true);
|
||||
if (!url)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
const nsACString *specPtr = &spec;
|
||||
|
||||
#if defined(XP_WIN)
|
||||
nsAutoCString buf;
|
||||
if (net_NormalizeFileURL(spec, buf))
|
||||
specPtr = &buf;
|
||||
#endif
|
||||
|
||||
nsresult rv = url->Init(nsIStandardURL::URLTYPE_NO_AUTHORITY, -1,
|
||||
*specPtr, charset, baseURI);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return CallQueryInterface(url, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::NewChannel2(nsIURI* uri,
|
||||
nsILoadInfo* aLoadInfo,
|
||||
nsIChannel** result)
|
||||
{
|
||||
nsFileChannel *chan = new nsFileChannel(uri);
|
||||
if (!chan)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(chan);
|
||||
|
||||
nsresult rv = chan->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_RELEASE(chan);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// set the loadInfo on the new channel
|
||||
rv = chan->SetLoadInfo(aLoadInfo);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_RELEASE(chan);
|
||||
return rv;
|
||||
}
|
||||
|
||||
*result = chan;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::NewChannel(nsIURI *uri, nsIChannel **result)
|
||||
{
|
||||
return NewChannel2(uri, nullptr, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *result)
|
||||
{
|
||||
// don't override anything.
|
||||
*result = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIFileProtocolHandler methods:
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::NewFileURI(nsIFile *file, nsIURI **result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(file);
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIFileURL> url = new nsStandardURL(true);
|
||||
if (!url)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
// NOTE: the origin charset is assigned the value of the platform
|
||||
// charset by the SetFile method.
|
||||
rv = url->SetFile(file);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return CallQueryInterface(url, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::GetURLSpecFromFile(nsIFile *file, nsACString &result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(file);
|
||||
return net_GetURLSpecFromFile(file, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::GetURLSpecFromActualFile(nsIFile *file,
|
||||
nsACString &result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(file);
|
||||
return net_GetURLSpecFromActualFile(file, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::GetURLSpecFromDir(nsIFile *file, nsACString &result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(file);
|
||||
return net_GetURLSpecFromDir(file, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFileProtocolHandler::GetFileFromURLSpec(const nsACString &spec, nsIFile **result)
|
||||
{
|
||||
return net_GetFileFromURLSpec(spec, result);
|
||||
}
|
||||
27
netwerk/protocol/file/nsFileProtocolHandler.h
Normal file
27
netwerk/protocol/file/nsFileProtocolHandler.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 nsFileProtocolHandler_h__
|
||||
#define nsFileProtocolHandler_h__
|
||||
|
||||
#include "nsIFileProtocolHandler.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
class nsFileProtocolHandler : public nsIFileProtocolHandler
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
virtual ~nsFileProtocolHandler() {}
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIPROTOCOLHANDLER
|
||||
NS_DECL_NSIFILEPROTOCOLHANDLER
|
||||
|
||||
nsFileProtocolHandler();
|
||||
|
||||
MOZ_MUST_USE nsresult Init();
|
||||
};
|
||||
|
||||
#endif // !nsFileProtocolHandler_h__
|
||||
17
netwerk/protocol/file/nsIFileChannel.idl
Normal file
17
netwerk/protocol/file/nsIFileChannel.idl
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIFile;
|
||||
|
||||
/**
|
||||
* nsIFileChannel
|
||||
*/
|
||||
[scriptable, uuid(06169120-136d-45a5-b535-498f1f755ab7)]
|
||||
interface nsIFileChannel : nsISupports
|
||||
{
|
||||
readonly attribute nsIFile file;
|
||||
};
|
||||
65
netwerk/protocol/file/nsIFileProtocolHandler.idl
Normal file
65
netwerk/protocol/file/nsIFileProtocolHandler.idl
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsIProtocolHandler.idl"
|
||||
|
||||
interface nsIFile;
|
||||
|
||||
[scriptable, uuid(1fb25bd5-4354-4dcd-8d97-621b7b3ed2e4)]
|
||||
interface nsIFileProtocolHandler : nsIProtocolHandler
|
||||
{
|
||||
/**
|
||||
* This method constructs a new file URI
|
||||
*
|
||||
* @param aFile nsIFile
|
||||
* @return reference to a new nsIURI object
|
||||
*/
|
||||
nsIURI newFileURI(in nsIFile aFile);
|
||||
|
||||
/**
|
||||
* Converts the nsIFile to the corresponding URL string. NOTE: under
|
||||
* some platforms this is a lossy conversion (e.g., Mac Carbon build).
|
||||
* If the nsIFile is a local file, then the result will be a file://
|
||||
* URL string.
|
||||
*
|
||||
* The resulting string may contain URL-escaped characters.
|
||||
* NOTE: Callers should use getURLSpecFromActualFile or
|
||||
* getURLSpecFromDirFile if possible, for performance reasons.
|
||||
*/
|
||||
AUTF8String getURLSpecFromFile(in nsIFile file);
|
||||
|
||||
/**
|
||||
* Converts the nsIFile to the corresponding URL string. Should
|
||||
* only be called on files which are not directories. Otherwise
|
||||
* identical to getURLSpecFromFile, but is usually more efficient.
|
||||
* WARNING: This restriction may not be enforced at runtime!
|
||||
*/
|
||||
AUTF8String getURLSpecFromActualFile(in nsIFile file);
|
||||
|
||||
/**
|
||||
* Converts the nsIFile to the corresponding URL string. Should
|
||||
* only be called on files which are directories. Otherwise
|
||||
* identical to getURLSpecFromFile, but is usually more efficient.
|
||||
* WARNING: This restriction may not be enforced at runtime!
|
||||
*/
|
||||
AUTF8String getURLSpecFromDir(in nsIFile file);
|
||||
|
||||
/**
|
||||
* Converts the URL string into the corresponding nsIFile if possible.
|
||||
* A local file will be created if the URL string begins with file://.
|
||||
*/
|
||||
nsIFile getFileFromURLSpec(in AUTF8String url);
|
||||
|
||||
/**
|
||||
* Takes a local file and tries to interpret it as an internet shortcut
|
||||
* (e.g. .url files on windows).
|
||||
* @param file The local file to read
|
||||
* @return The URI the file refers to
|
||||
*
|
||||
* @throw NS_ERROR_NOT_AVAILABLE if the OS does not support such files.
|
||||
* @throw NS_ERROR_NOT_AVAILABLE if this file is not an internet shortcut.
|
||||
*/
|
||||
nsIURI readURLFile(in nsIFile file);
|
||||
};
|
||||
932
netwerk/protocol/ftp/FTPChannelChild.cpp
Normal file
932
netwerk/protocol/ftp/FTPChannelChild.cpp
Normal file
|
|
@ -0,0 +1,932 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/. */
|
||||
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/net/ChannelDiverterChild.h"
|
||||
#include "mozilla/net/FTPChannelChild.h"
|
||||
#include "mozilla/dom/TabChild.h"
|
||||
#include "nsFtpProtocolHandler.h"
|
||||
#include "nsITabChild.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
#include "mozilla/ipc/URIUtils.h"
|
||||
#include "SerializedLoadContext.h"
|
||||
#include "mozilla/ipc/BackgroundUtils.h"
|
||||
#include "nsIPrompt.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
#undef LOG
|
||||
#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
FTPChannelChild::FTPChannelChild(nsIURI* uri)
|
||||
: mIPCOpen(false)
|
||||
, mUnknownDecoderInvolved(false)
|
||||
, mCanceled(false)
|
||||
, mSuspendCount(0)
|
||||
, mIsPending(false)
|
||||
, mLastModifiedTime(0)
|
||||
, mStartPos(0)
|
||||
, mDivertingToParent(false)
|
||||
, mFlushedForDiversion(false)
|
||||
, mSuspendSent(false)
|
||||
{
|
||||
LOG(("Creating FTPChannelChild @%x\n", this));
|
||||
// grab a reference to the handler to ensure that it doesn't go away.
|
||||
NS_ADDREF(gFtpHandler);
|
||||
SetURI(uri);
|
||||
mEventQ = new ChannelEventQueue(static_cast<nsIFTPChannel*>(this));
|
||||
|
||||
// We could support thread retargeting, but as long as we're being driven by
|
||||
// IPDL on the main thread it doesn't buy us anything.
|
||||
DisallowThreadRetargeting();
|
||||
}
|
||||
|
||||
FTPChannelChild::~FTPChannelChild()
|
||||
{
|
||||
LOG(("Destroying FTPChannelChild @%x\n", this));
|
||||
gFtpHandler->Release();
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelChild::AddIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(!mIPCOpen, "Attempt to retain more than one IPDL reference");
|
||||
mIPCOpen = true;
|
||||
AddRef();
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelChild::ReleaseIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference");
|
||||
mIPCOpen = false;
|
||||
Release();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelChild::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(FTPChannelChild,
|
||||
nsBaseChannel,
|
||||
nsIFTPChannel,
|
||||
nsIUploadChannel,
|
||||
nsIResumableChannel,
|
||||
nsIProxiedChannel,
|
||||
nsIChildChannel,
|
||||
nsIDivertableChannel)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::GetLastModifiedTime(PRTime* lastModifiedTime)
|
||||
{
|
||||
*lastModifiedTime = mLastModifiedTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::SetLastModifiedTime(PRTime lastModifiedTime)
|
||||
{
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID)
|
||||
{
|
||||
NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
|
||||
mStartPos = aStartPos;
|
||||
mEntityID = aEntityID;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::GetEntityID(nsACString& entityID)
|
||||
{
|
||||
entityID = mEntityID;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::GetProxyInfo(nsIProxyInfo** aProxyInfo)
|
||||
{
|
||||
DROP_DEAD();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::SetUploadStream(nsIInputStream* stream,
|
||||
const nsACString& contentType,
|
||||
int64_t contentLength)
|
||||
{
|
||||
NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
|
||||
mUploadStream = stream;
|
||||
// NOTE: contentLength is intentionally ignored here.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::GetUploadStream(nsIInputStream** stream)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(stream);
|
||||
*stream = mUploadStream;
|
||||
NS_IF_ADDREF(*stream);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::AsyncOpen(::nsIStreamListener* listener, nsISupports* aContext)
|
||||
{
|
||||
LOG(("FTPChannelChild::AsyncOpen [this=%p]\n", this));
|
||||
|
||||
NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
|
||||
NS_ENSURE_ARG_POINTER(listener);
|
||||
NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
|
||||
NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
|
||||
|
||||
// Port checked in parent, but duplicate here so we can return with error
|
||||
// immediately, as we've done since before e10s.
|
||||
nsresult rv;
|
||||
rv = NS_CheckPortSafety(nsBaseChannel::URI()); // Need to disambiguate,
|
||||
// because in the child ipdl,
|
||||
// a typedef URI is defined...
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
mozilla::dom::TabChild* tabChild = nullptr;
|
||||
nsCOMPtr<nsITabChild> iTabChild;
|
||||
NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
|
||||
NS_GET_IID(nsITabChild),
|
||||
getter_AddRefs(iTabChild));
|
||||
GetCallback(iTabChild);
|
||||
if (iTabChild) {
|
||||
tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get());
|
||||
}
|
||||
if (MissingRequiredTabChild(tabChild, "ftp")) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
mListener = listener;
|
||||
mListenerContext = aContext;
|
||||
|
||||
// add ourselves to the load group.
|
||||
if (mLoadGroup)
|
||||
mLoadGroup->AddRequest(this, nullptr);
|
||||
|
||||
OptionalInputStreamParams uploadStream;
|
||||
nsTArray<mozilla::ipc::FileDescriptor> fds;
|
||||
SerializeInputStream(mUploadStream, uploadStream, fds);
|
||||
|
||||
MOZ_ASSERT(fds.IsEmpty());
|
||||
|
||||
FTPChannelOpenArgs openArgs;
|
||||
SerializeURI(nsBaseChannel::URI(), openArgs.uri());
|
||||
openArgs.startPos() = mStartPos;
|
||||
openArgs.entityID() = mEntityID;
|
||||
openArgs.uploadStream() = uploadStream;
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo;
|
||||
GetLoadInfo(getter_AddRefs(loadInfo));
|
||||
rv = mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &openArgs.loadInfo());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
gNeckoChild->
|
||||
SendPFTPChannelConstructor(this, tabChild, IPC::SerializedLoadContext(this),
|
||||
openArgs);
|
||||
|
||||
// The socket transport layer in the chrome process now has a logical ref to
|
||||
// us until OnStopRequest is called.
|
||||
AddIPDLReference();
|
||||
|
||||
mIsPending = true;
|
||||
mWasOpened = true;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::IsPending(bool* result)
|
||||
{
|
||||
*result = mIsPending;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FTPChannelChild::OpenContentStream(bool async,
|
||||
nsIInputStream** stream,
|
||||
nsIChannel** channel)
|
||||
{
|
||||
NS_RUNTIMEABORT("FTPChannel*Child* should never have OpenContentStream called!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelChild::PFTPChannelChild
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class FTPStartRequestEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
FTPStartRequestEvent(FTPChannelChild* aChild,
|
||||
const nsresult& aChannelStatus,
|
||||
const int64_t& aContentLength,
|
||||
const nsCString& aContentType,
|
||||
const PRTime& aLastModified,
|
||||
const nsCString& aEntityID,
|
||||
const URIParams& aURI)
|
||||
: mChild(aChild)
|
||||
, mChannelStatus(aChannelStatus)
|
||||
, mContentLength(aContentLength)
|
||||
, mContentType(aContentType)
|
||||
, mLastModified(aLastModified)
|
||||
, mEntityID(aEntityID)
|
||||
, mURI(aURI)
|
||||
{
|
||||
}
|
||||
void Run()
|
||||
{
|
||||
mChild->DoOnStartRequest(mChannelStatus, mContentLength, mContentType,
|
||||
mLastModified, mEntityID, mURI);
|
||||
}
|
||||
|
||||
private:
|
||||
FTPChannelChild* mChild;
|
||||
nsresult mChannelStatus;
|
||||
int64_t mContentLength;
|
||||
nsCString mContentType;
|
||||
PRTime mLastModified;
|
||||
nsCString mEntityID;
|
||||
URIParams mURI;
|
||||
};
|
||||
|
||||
bool
|
||||
FTPChannelChild::RecvOnStartRequest(const nsresult& aChannelStatus,
|
||||
const int64_t& aContentLength,
|
||||
const nsCString& aContentType,
|
||||
const PRTime& aLastModified,
|
||||
const nsCString& aEntityID,
|
||||
const URIParams& aURI)
|
||||
{
|
||||
// mFlushedForDiversion and mDivertingToParent should NEVER be set at this
|
||||
// stage, as they are set in the listener's OnStartRequest.
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
"mFlushedForDiversion should be unset before OnStartRequest!");
|
||||
MOZ_RELEASE_ASSERT(!mDivertingToParent,
|
||||
"mDivertingToParent should be unset before OnStartRequest!");
|
||||
|
||||
LOG(("FTPChannelChild::RecvOnStartRequest [this=%p]\n", this));
|
||||
|
||||
mEventQ->RunOrEnqueue(new FTPStartRequestEvent(this, aChannelStatus,
|
||||
aContentLength, aContentType,
|
||||
aLastModified, aEntityID,
|
||||
aURI));
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelChild::DoOnStartRequest(const nsresult& aChannelStatus,
|
||||
const int64_t& aContentLength,
|
||||
const nsCString& aContentType,
|
||||
const PRTime& aLastModified,
|
||||
const nsCString& aEntityID,
|
||||
const URIParams& aURI)
|
||||
{
|
||||
LOG(("FTPChannelChild::DoOnStartRequest [this=%p]\n", this));
|
||||
|
||||
// mFlushedForDiversion and mDivertingToParent should NEVER be set at this
|
||||
// stage, as they are set in the listener's OnStartRequest.
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
"mFlushedForDiversion should be unset before OnStartRequest!");
|
||||
MOZ_RELEASE_ASSERT(!mDivertingToParent,
|
||||
"mDivertingToParent should be unset before OnStartRequest!");
|
||||
|
||||
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
|
||||
mStatus = aChannelStatus;
|
||||
}
|
||||
|
||||
mContentLength = aContentLength;
|
||||
SetContentType(aContentType);
|
||||
mLastModifiedTime = aLastModified;
|
||||
mEntityID = aEntityID;
|
||||
|
||||
nsCString spec;
|
||||
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
|
||||
nsresult rv = uri->GetSpec(spec);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = nsBaseChannel::URI()->SetSpec(spec);
|
||||
if (NS_FAILED(rv)) {
|
||||
Cancel(rv);
|
||||
}
|
||||
} else {
|
||||
Cancel(rv);
|
||||
}
|
||||
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
rv = mListener->OnStartRequest(this, mListenerContext);
|
||||
if (NS_FAILED(rv))
|
||||
Cancel(rv);
|
||||
|
||||
if (mDivertingToParent) {
|
||||
mListener = nullptr;
|
||||
mListenerContext = nullptr;
|
||||
if (mLoadGroup) {
|
||||
mLoadGroup->RemoveRequest(this, nullptr, mStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FTPDataAvailableEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
FTPDataAvailableEvent(FTPChannelChild* aChild,
|
||||
const nsresult& aChannelStatus,
|
||||
const nsCString& aData,
|
||||
const uint64_t& aOffset,
|
||||
const uint32_t& aCount)
|
||||
: mChild(aChild)
|
||||
, mChannelStatus(aChannelStatus)
|
||||
, mData(aData)
|
||||
, mOffset(aOffset)
|
||||
, mCount(aCount)
|
||||
{
|
||||
}
|
||||
void Run()
|
||||
{
|
||||
mChild->DoOnDataAvailable(mChannelStatus, mData, mOffset, mCount);
|
||||
}
|
||||
|
||||
private:
|
||||
FTPChannelChild* mChild;
|
||||
nsresult mChannelStatus;
|
||||
nsCString mData;
|
||||
uint64_t mOffset;
|
||||
uint32_t mCount;
|
||||
};
|
||||
|
||||
bool
|
||||
FTPChannelChild::RecvOnDataAvailable(const nsresult& channelStatus,
|
||||
const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
"Should not be receiving any more callbacks from parent!");
|
||||
|
||||
LOG(("FTPChannelChild::RecvOnDataAvailable [this=%p]\n", this));
|
||||
|
||||
mEventQ->RunOrEnqueue(new FTPDataAvailableEvent(this, channelStatus, data,
|
||||
offset, count),
|
||||
mDivertingToParent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class MaybeDivertOnDataFTPEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
MaybeDivertOnDataFTPEvent(FTPChannelChild* child,
|
||||
const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count)
|
||||
: mChild(child)
|
||||
, mData(data)
|
||||
, mOffset(offset)
|
||||
, mCount(count) {}
|
||||
|
||||
void Run()
|
||||
{
|
||||
mChild->MaybeDivertOnData(mData, mOffset, mCount);
|
||||
}
|
||||
|
||||
private:
|
||||
FTPChannelChild* mChild;
|
||||
nsCString mData;
|
||||
uint64_t mOffset;
|
||||
uint32_t mCount;
|
||||
};
|
||||
|
||||
void
|
||||
FTPChannelChild::MaybeDivertOnData(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count)
|
||||
{
|
||||
if (mDivertingToParent) {
|
||||
SendDivertOnDataAvailable(data, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelChild::DoOnDataAvailable(const nsresult& channelStatus,
|
||||
const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count)
|
||||
{
|
||||
LOG(("FTPChannelChild::DoOnDataAvailable [this=%p]\n", this));
|
||||
|
||||
if (!mCanceled && NS_SUCCEEDED(mStatus)) {
|
||||
mStatus = channelStatus;
|
||||
}
|
||||
|
||||
if (mDivertingToParent) {
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
"Should not be processing any more callbacks from parent!");
|
||||
|
||||
SendDivertOnDataAvailable(data, offset, count);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCanceled)
|
||||
return;
|
||||
|
||||
if (mUnknownDecoderInvolved) {
|
||||
mUnknownDecoderEventQ.AppendElement(
|
||||
MakeUnique<MaybeDivertOnDataFTPEvent>(this, data, offset, count));
|
||||
}
|
||||
|
||||
// NOTE: the OnDataAvailable contract requires the client to read all the data
|
||||
// in the inputstream. This code relies on that ('data' will go away after
|
||||
// this function). Apparently the previous, non-e10s behavior was to actually
|
||||
// support only reading part of the data, allowing later calls to read the
|
||||
// rest.
|
||||
nsCOMPtr<nsIInputStream> stringStream;
|
||||
nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream),
|
||||
data.get(),
|
||||
count,
|
||||
NS_ASSIGNMENT_DEPEND);
|
||||
if (NS_FAILED(rv)) {
|
||||
Cancel(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
rv = mListener->OnDataAvailable(this, mListenerContext,
|
||||
stringStream, offset, count);
|
||||
if (NS_FAILED(rv))
|
||||
Cancel(rv);
|
||||
stringStream->Close();
|
||||
}
|
||||
|
||||
class FTPStopRequestEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
FTPStopRequestEvent(FTPChannelChild* aChild,
|
||||
const nsresult& aChannelStatus,
|
||||
const nsCString &aErrorMsg,
|
||||
bool aUseUTF8)
|
||||
: mChild(aChild)
|
||||
, mChannelStatus(aChannelStatus)
|
||||
, mErrorMsg(aErrorMsg)
|
||||
, mUseUTF8(aUseUTF8)
|
||||
{
|
||||
}
|
||||
void Run()
|
||||
{
|
||||
mChild->DoOnStopRequest(mChannelStatus, mErrorMsg, mUseUTF8);
|
||||
}
|
||||
|
||||
private:
|
||||
FTPChannelChild* mChild;
|
||||
nsresult mChannelStatus;
|
||||
nsCString mErrorMsg;
|
||||
bool mUseUTF8;
|
||||
};
|
||||
|
||||
bool
|
||||
FTPChannelChild::RecvOnStopRequest(const nsresult& aChannelStatus,
|
||||
const nsCString &aErrorMsg,
|
||||
const bool &aUseUTF8)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
"Should not be receiving any more callbacks from parent!");
|
||||
|
||||
LOG(("FTPChannelChild::RecvOnStopRequest [this=%p status=%x]\n",
|
||||
this, aChannelStatus));
|
||||
|
||||
mEventQ->RunOrEnqueue(new FTPStopRequestEvent(this, aChannelStatus, aErrorMsg,
|
||||
aUseUTF8));
|
||||
return true;
|
||||
}
|
||||
|
||||
class nsFtpChildAsyncAlert : public Runnable
|
||||
{
|
||||
public:
|
||||
nsFtpChildAsyncAlert(nsIPrompt *aPrompter, nsString aResponseMsg)
|
||||
: mPrompter(aPrompter)
|
||||
, mResponseMsg(aResponseMsg)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsFtpChildAsyncAlert);
|
||||
}
|
||||
protected:
|
||||
virtual ~nsFtpChildAsyncAlert()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsFtpChildAsyncAlert);
|
||||
}
|
||||
public:
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
if (mPrompter) {
|
||||
mPrompter->Alert(nullptr, mResponseMsg.get());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsCOMPtr<nsIPrompt> mPrompter;
|
||||
nsString mResponseMsg;
|
||||
};
|
||||
|
||||
class MaybeDivertOnStopFTPEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
MaybeDivertOnStopFTPEvent(FTPChannelChild* child,
|
||||
const nsresult& aChannelStatus)
|
||||
: mChild(child)
|
||||
, mChannelStatus(aChannelStatus) {}
|
||||
|
||||
void Run()
|
||||
{
|
||||
mChild->MaybeDivertOnStop(mChannelStatus);
|
||||
}
|
||||
|
||||
private:
|
||||
FTPChannelChild* mChild;
|
||||
nsresult mChannelStatus;
|
||||
};
|
||||
|
||||
void
|
||||
FTPChannelChild::MaybeDivertOnStop(const nsresult& aChannelStatus)
|
||||
{
|
||||
if (mDivertingToParent) {
|
||||
SendDivertOnStopRequest(aChannelStatus);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelChild::DoOnStopRequest(const nsresult& aChannelStatus,
|
||||
const nsCString &aErrorMsg,
|
||||
bool aUseUTF8)
|
||||
{
|
||||
LOG(("FTPChannelChild::DoOnStopRequest [this=%p status=%x]\n",
|
||||
this, aChannelStatus));
|
||||
|
||||
if (mDivertingToParent) {
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
"Should not be processing any more callbacks from parent!");
|
||||
|
||||
SendDivertOnStopRequest(aChannelStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mCanceled)
|
||||
mStatus = aChannelStatus;
|
||||
|
||||
if (mUnknownDecoderInvolved) {
|
||||
mUnknownDecoderEventQ.AppendElement(
|
||||
MakeUnique<MaybeDivertOnStopFTPEvent>(this, aChannelStatus));
|
||||
}
|
||||
|
||||
{ // Ensure that all queued ipdl events are dispatched before
|
||||
// we initiate protocol deletion below.
|
||||
mIsPending = false;
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
(void)mListener->OnStopRequest(this, mListenerContext, aChannelStatus);
|
||||
|
||||
if (NS_FAILED(aChannelStatus) && !aErrorMsg.IsEmpty()) {
|
||||
nsCOMPtr<nsIPrompt> prompter;
|
||||
GetCallback(prompter);
|
||||
if (prompter) {
|
||||
nsCOMPtr<nsIRunnable> alertEvent;
|
||||
if (aUseUTF8) {
|
||||
alertEvent = new nsFtpChildAsyncAlert(prompter,
|
||||
NS_ConvertUTF8toUTF16(aErrorMsg));
|
||||
} else {
|
||||
alertEvent = new nsFtpChildAsyncAlert(prompter,
|
||||
NS_ConvertASCIItoUTF16(aErrorMsg));
|
||||
}
|
||||
NS_DispatchToMainThread(alertEvent);
|
||||
}
|
||||
}
|
||||
|
||||
mListener = nullptr;
|
||||
mListenerContext = nullptr;
|
||||
|
||||
if (mLoadGroup)
|
||||
mLoadGroup->RemoveRequest(this, nullptr, aChannelStatus);
|
||||
}
|
||||
|
||||
// This calls NeckoChild::DeallocPFTPChannelChild(), which deletes |this| if IPDL
|
||||
// holds the last reference. Don't rely on |this| existing after here!
|
||||
Send__delete__(this);
|
||||
}
|
||||
|
||||
class FTPFailedAsyncOpenEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
FTPFailedAsyncOpenEvent(FTPChannelChild* aChild, nsresult aStatus)
|
||||
: mChild(aChild), mStatus(aStatus) {}
|
||||
void Run() { mChild->DoFailedAsyncOpen(mStatus); }
|
||||
private:
|
||||
FTPChannelChild* mChild;
|
||||
nsresult mStatus;
|
||||
};
|
||||
|
||||
bool
|
||||
FTPChannelChild::RecvFailedAsyncOpen(const nsresult& statusCode)
|
||||
{
|
||||
LOG(("FTPChannelChild::RecvFailedAsyncOpen [this=%p status=%x]\n",
|
||||
this, statusCode));
|
||||
mEventQ->RunOrEnqueue(new FTPFailedAsyncOpenEvent(this, statusCode));
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelChild::DoFailedAsyncOpen(const nsresult& statusCode)
|
||||
{
|
||||
LOG(("FTPChannelChild::DoFailedAsyncOpen [this=%p status=%x]\n",
|
||||
this, statusCode));
|
||||
mStatus = statusCode;
|
||||
|
||||
if (mLoadGroup)
|
||||
mLoadGroup->RemoveRequest(this, nullptr, statusCode);
|
||||
|
||||
if (mListener) {
|
||||
mListener->OnStartRequest(this, mListenerContext);
|
||||
mIsPending = false;
|
||||
mListener->OnStopRequest(this, mListenerContext, statusCode);
|
||||
} else {
|
||||
mIsPending = false;
|
||||
}
|
||||
|
||||
mListener = nullptr;
|
||||
mListenerContext = nullptr;
|
||||
|
||||
if (mIPCOpen)
|
||||
Send__delete__(this);
|
||||
}
|
||||
|
||||
class FTPFlushedForDiversionEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
explicit FTPFlushedForDiversionEvent(FTPChannelChild* aChild)
|
||||
: mChild(aChild)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aChild);
|
||||
}
|
||||
|
||||
void Run()
|
||||
{
|
||||
mChild->FlushedForDiversion();
|
||||
}
|
||||
private:
|
||||
FTPChannelChild* mChild;
|
||||
};
|
||||
|
||||
bool
|
||||
FTPChannelChild::RecvFlushedForDiversion()
|
||||
{
|
||||
LOG(("FTPChannelChild::RecvFlushedForDiversion [this=%p]\n", this));
|
||||
MOZ_ASSERT(mDivertingToParent);
|
||||
|
||||
mEventQ->RunOrEnqueue(new FTPFlushedForDiversionEvent(this), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelChild::FlushedForDiversion()
|
||||
{
|
||||
LOG(("FTPChannelChild::FlushedForDiversion [this=%p]\n", this));
|
||||
MOZ_RELEASE_ASSERT(mDivertingToParent);
|
||||
|
||||
// Once this is set, it should not be unset before FTPChannelChild is taken
|
||||
// down. After it is set, no OnStart/OnData/OnStop callbacks should be
|
||||
// received from the parent channel, nor dequeued from the ChannelEventQueue.
|
||||
mFlushedForDiversion = true;
|
||||
|
||||
SendDivertComplete();
|
||||
}
|
||||
|
||||
bool
|
||||
FTPChannelChild::RecvDivertMessages()
|
||||
{
|
||||
LOG(("FTPChannelChild::RecvDivertMessages [this=%p]\n", this));
|
||||
MOZ_RELEASE_ASSERT(mDivertingToParent);
|
||||
MOZ_RELEASE_ASSERT(mSuspendCount > 0);
|
||||
|
||||
// DivertTo() has been called on parent, so we can now start sending queued
|
||||
// IPDL messages back to parent listener.
|
||||
if (NS_WARN_IF(NS_FAILED(Resume()))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class FTPDeleteSelfEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
explicit FTPDeleteSelfEvent(FTPChannelChild* aChild)
|
||||
: mChild(aChild) {}
|
||||
void Run() { mChild->DoDeleteSelf(); }
|
||||
private:
|
||||
FTPChannelChild* mChild;
|
||||
};
|
||||
|
||||
bool
|
||||
FTPChannelChild::RecvDeleteSelf()
|
||||
{
|
||||
mEventQ->RunOrEnqueue(new FTPDeleteSelfEvent(this));
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelChild::DoDeleteSelf()
|
||||
{
|
||||
if (mIPCOpen)
|
||||
Send__delete__(this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::Cancel(nsresult status)
|
||||
{
|
||||
LOG(("FTPChannelChild::Cancel [this=%p]\n", this));
|
||||
if (mCanceled)
|
||||
return NS_OK;
|
||||
|
||||
mCanceled = true;
|
||||
mStatus = status;
|
||||
if (mIPCOpen)
|
||||
SendCancel(status);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::Suspend()
|
||||
{
|
||||
NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
LOG(("FTPChannelChild::Suspend [this=%p]\n", this));
|
||||
|
||||
// SendSuspend only once, when suspend goes from 0 to 1.
|
||||
// Don't SendSuspend at all if we're diverting callbacks to the parent;
|
||||
// suspend will be called at the correct time in the parent itself.
|
||||
if (!mSuspendCount++ && !mDivertingToParent) {
|
||||
SendSuspend();
|
||||
mSuspendSent = true;
|
||||
}
|
||||
mEventQ->Suspend();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::Resume()
|
||||
{
|
||||
NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
LOG(("FTPChannelChild::Resume [this=%p]\n", this));
|
||||
|
||||
// SendResume only once, when suspend count drops to 0.
|
||||
// Don't SendResume at all if we're diverting callbacks to the parent (unless
|
||||
// suspend was sent earlier); otherwise, resume will be called at the correct
|
||||
// time in the parent itself.
|
||||
if (!--mSuspendCount && (!mDivertingToParent || mSuspendSent)) {
|
||||
SendResume();
|
||||
}
|
||||
mEventQ->Resume();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelChild::nsIChildChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::ConnectParent(uint32_t id)
|
||||
{
|
||||
LOG(("FTPChannelChild::ConnectParent [this=%p]\n", this));
|
||||
|
||||
mozilla::dom::TabChild* tabChild = nullptr;
|
||||
nsCOMPtr<nsITabChild> iTabChild;
|
||||
NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
|
||||
NS_GET_IID(nsITabChild),
|
||||
getter_AddRefs(iTabChild));
|
||||
GetCallback(iTabChild);
|
||||
if (iTabChild) {
|
||||
tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get());
|
||||
}
|
||||
|
||||
// The socket transport in the chrome process now holds a logical ref to us
|
||||
// until OnStopRequest, or we do a redirect, or we hit an IPDL error.
|
||||
AddIPDLReference();
|
||||
|
||||
FTPChannelConnectArgs connectArgs(id);
|
||||
|
||||
if (!gNeckoChild->SendPFTPChannelConstructor(this, tabChild,
|
||||
IPC::SerializedLoadContext(this),
|
||||
connectArgs)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::CompleteRedirectSetup(nsIStreamListener *listener,
|
||||
nsISupports *aContext)
|
||||
{
|
||||
LOG(("FTPChannelChild::CompleteRedirectSetup [this=%p]\n", this));
|
||||
|
||||
NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
|
||||
NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
|
||||
|
||||
mIsPending = true;
|
||||
mWasOpened = true;
|
||||
mListener = listener;
|
||||
mListenerContext = aContext;
|
||||
|
||||
// add ourselves to the load group.
|
||||
if (mLoadGroup)
|
||||
mLoadGroup->AddRequest(this, nullptr);
|
||||
|
||||
// We already have an open IPDL connection to the parent. If on-modify-request
|
||||
// listeners or load group observers canceled us, let the parent handle it
|
||||
// and send it back to us naturally.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelChild::nsIDivertableChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::DivertToParent(ChannelDiverterChild **aChild)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aChild);
|
||||
MOZ_RELEASE_ASSERT(gNeckoChild);
|
||||
MOZ_RELEASE_ASSERT(!mDivertingToParent);
|
||||
|
||||
LOG(("FTPChannelChild::DivertToParent [this=%p]\n", this));
|
||||
|
||||
// We must fail DivertToParent() if there's no parent end of the channel (and
|
||||
// won't be!) due to early failure.
|
||||
if (NS_FAILED(mStatus) && !mIPCOpen) {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
nsresult rv = Suspend();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Once this is set, it should not be unset before the child is taken down.
|
||||
mDivertingToParent = true;
|
||||
|
||||
PChannelDiverterChild* diverter =
|
||||
gNeckoChild->SendPChannelDiverterConstructor(this);
|
||||
MOZ_RELEASE_ASSERT(diverter);
|
||||
|
||||
*aChild = static_cast<ChannelDiverterChild*>(diverter);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::UnknownDecoderInvolvedKeepData()
|
||||
{
|
||||
mUnknownDecoderInvolved = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::UnknownDecoderInvolvedOnStartRequestCalled()
|
||||
{
|
||||
mUnknownDecoderInvolved = false;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (mDivertingToParent) {
|
||||
rv = mEventQ->PrependEvents(mUnknownDecoderEventQ);
|
||||
}
|
||||
mUnknownDecoderEventQ.Clear();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::GetDivertingToParent(bool* aDiverting)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aDiverting);
|
||||
*aDiverting = mDivertingToParent;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
164
netwerk/protocol/ftp/FTPChannelChild.h
Normal file
164
netwerk/protocol/ftp/FTPChannelChild.h
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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_FTPChannelChild_h
|
||||
#define mozilla_net_FTPChannelChild_h
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/net/PFTPChannelChild.h"
|
||||
#include "mozilla/net/ChannelEventQueue.h"
|
||||
#include "nsBaseChannel.h"
|
||||
#include "nsIFTPChannel.h"
|
||||
#include "nsIUploadChannel.h"
|
||||
#include "nsIProxiedChannel.h"
|
||||
#include "nsIResumableChannel.h"
|
||||
#include "nsIChildChannel.h"
|
||||
#include "nsIDivertableChannel.h"
|
||||
|
||||
#include "nsIStreamListener.h"
|
||||
#include "PrivateBrowsingChannel.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
// This class inherits logic from nsBaseChannel that is not needed for an
|
||||
// e10s child channel, but it works. At some point we could slice up
|
||||
// nsBaseChannel and have a new class that has only the common logic for
|
||||
// nsFTPChannel/FTPChannelChild.
|
||||
|
||||
class FTPChannelChild final : public PFTPChannelChild
|
||||
, public nsBaseChannel
|
||||
, public nsIFTPChannel
|
||||
, public nsIUploadChannel
|
||||
, public nsIResumableChannel
|
||||
, public nsIProxiedChannel
|
||||
, public nsIChildChannel
|
||||
, public nsIDivertableChannel
|
||||
{
|
||||
public:
|
||||
typedef ::nsIStreamListener nsIStreamListener;
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIFTPCHANNEL
|
||||
NS_DECL_NSIUPLOADCHANNEL
|
||||
NS_DECL_NSIRESUMABLECHANNEL
|
||||
NS_DECL_NSIPROXIEDCHANNEL
|
||||
NS_DECL_NSICHILDCHANNEL
|
||||
NS_DECL_NSIDIVERTABLECHANNEL
|
||||
|
||||
NS_IMETHOD Cancel(nsresult status) override;
|
||||
NS_IMETHOD Suspend() override;
|
||||
NS_IMETHOD Resume() override;
|
||||
|
||||
explicit FTPChannelChild(nsIURI* uri);
|
||||
|
||||
void AddIPDLReference();
|
||||
void ReleaseIPDLReference();
|
||||
|
||||
NS_IMETHOD AsyncOpen(nsIStreamListener* listener, nsISupports* aContext) override;
|
||||
|
||||
// Note that we handle this ourselves, overriding the nsBaseChannel
|
||||
// default behavior, in order to be e10s-friendly.
|
||||
NS_IMETHOD IsPending(bool* result) override;
|
||||
|
||||
nsresult OpenContentStream(bool async,
|
||||
nsIInputStream** stream,
|
||||
nsIChannel** channel) override;
|
||||
|
||||
bool IsSuspended();
|
||||
|
||||
void FlushedForDiversion();
|
||||
|
||||
protected:
|
||||
virtual ~FTPChannelChild();
|
||||
|
||||
bool RecvOnStartRequest(const nsresult& aChannelStatus,
|
||||
const int64_t& aContentLength,
|
||||
const nsCString& aContentType,
|
||||
const PRTime& aLastModified,
|
||||
const nsCString& aEntityID,
|
||||
const URIParams& aURI) override;
|
||||
bool RecvOnDataAvailable(const nsresult& channelStatus,
|
||||
const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count) override;
|
||||
bool RecvOnStopRequest(const nsresult& channelStatus,
|
||||
const nsCString &aErrorMsg,
|
||||
const bool &aUseUTF8) override;
|
||||
bool RecvFailedAsyncOpen(const nsresult& statusCode) override;
|
||||
bool RecvFlushedForDiversion() override;
|
||||
bool RecvDivertMessages() override;
|
||||
bool RecvDeleteSelf() override;
|
||||
|
||||
void DoOnStartRequest(const nsresult& aChannelStatus,
|
||||
const int64_t& aContentLength,
|
||||
const nsCString& aContentType,
|
||||
const PRTime& aLastModified,
|
||||
const nsCString& aEntityID,
|
||||
const URIParams& aURI);
|
||||
void DoOnDataAvailable(const nsresult& channelStatus,
|
||||
const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count);
|
||||
void MaybeDivertOnData(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count);
|
||||
void MaybeDivertOnStop(const nsresult& statusCode);
|
||||
void DoOnStopRequest(const nsresult& statusCode,
|
||||
const nsCString &aErrorMsg,
|
||||
bool aUseUTF8);
|
||||
void DoFailedAsyncOpen(const nsresult& statusCode);
|
||||
void DoDeleteSelf();
|
||||
|
||||
friend class FTPStartRequestEvent;
|
||||
friend class FTPDataAvailableEvent;
|
||||
friend class MaybeDivertOnDataFTPEvent;
|
||||
friend class FTPStopRequestEvent;
|
||||
friend class MaybeDivertOnStopFTPEvent;
|
||||
friend class FTPFailedAsyncOpenEvent;
|
||||
friend class FTPDeleteSelfEvent;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIInputStream> mUploadStream;
|
||||
|
||||
bool mIPCOpen;
|
||||
RefPtr<ChannelEventQueue> mEventQ;
|
||||
|
||||
// If nsUnknownDecoder is involved we queue onDataAvailable (and possibly
|
||||
// OnStopRequest) so that we can divert them if needed when the listener's
|
||||
// OnStartRequest is finally called
|
||||
nsTArray<UniquePtr<ChannelEvent>> mUnknownDecoderEventQ;
|
||||
bool mUnknownDecoderInvolved;
|
||||
|
||||
bool mCanceled;
|
||||
uint32_t mSuspendCount;
|
||||
bool mIsPending;
|
||||
|
||||
PRTime mLastModifiedTime;
|
||||
uint64_t mStartPos;
|
||||
nsCString mEntityID;
|
||||
|
||||
// Once set, OnData and possibly OnStop will be diverted to the parent.
|
||||
bool mDivertingToParent;
|
||||
// Once set, no OnStart/OnData/OnStop callbacks should be received from the
|
||||
// parent channel, nor dequeued from the ChannelEventQueue.
|
||||
bool mFlushedForDiversion;
|
||||
// Set if SendSuspend is called. Determines if SendResume is needed when
|
||||
// diverting callbacks to parent.
|
||||
bool mSuspendSent;
|
||||
};
|
||||
|
||||
inline bool
|
||||
FTPChannelChild::IsSuspended()
|
||||
{
|
||||
return mSuspendCount != 0;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_FTPChannelChild_h
|
||||
896
netwerk/protocol/ftp/FTPChannelParent.cpp
Normal file
896
netwerk/protocol/ftp/FTPChannelParent.cpp
Normal file
|
|
@ -0,0 +1,896 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/. */
|
||||
|
||||
#include "mozilla/net/FTPChannelParent.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "mozilla/net/ChannelEventQueue.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
#include "nsFTPChannel.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsQueryObject.h"
|
||||
#include "nsFtpProtocolHandler.h"
|
||||
#include "nsIAuthPrompt.h"
|
||||
#include "nsIAuthPromptProvider.h"
|
||||
#include "nsIEncodedChannel.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsIForcePendingChannel.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
#include "mozilla/ipc/URIUtils.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "SerializedLoadContext.h"
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "mozilla/ipc/BackgroundUtils.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
#undef LOG
|
||||
#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
FTPChannelParent::FTPChannelParent(const PBrowserOrId& aIframeEmbedding,
|
||||
nsILoadContext* aLoadContext,
|
||||
PBOverrideStatus aOverrideStatus)
|
||||
: mIPCClosed(false)
|
||||
, mLoadContext(aLoadContext)
|
||||
, mPBOverride(aOverrideStatus)
|
||||
, mStatus(NS_OK)
|
||||
, mDivertingFromChild(false)
|
||||
, mDivertedOnStartRequest(false)
|
||||
, mSuspendedForDiversion(false)
|
||||
, mUseUTF8(false)
|
||||
{
|
||||
nsIProtocolHandler* handler;
|
||||
CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &handler);
|
||||
MOZ_ASSERT(handler, "no ftp handler");
|
||||
|
||||
if (aIframeEmbedding.type() == PBrowserOrId::TPBrowserParent) {
|
||||
mTabParent = static_cast<dom::TabParent*>(aIframeEmbedding.get_PBrowserParent());
|
||||
}
|
||||
|
||||
mEventQ = new ChannelEventQueue(static_cast<nsIParentChannel*>(this));
|
||||
}
|
||||
|
||||
FTPChannelParent::~FTPChannelParent()
|
||||
{
|
||||
gFtpHandler->Release();
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
// We may still have refcount>0 if the channel hasn't called OnStopRequest
|
||||
// yet, but we must not send any more msgs to child.
|
||||
mIPCClosed = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelParent::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS(FTPChannelParent,
|
||||
nsIStreamListener,
|
||||
nsIParentChannel,
|
||||
nsIInterfaceRequestor,
|
||||
nsIRequestObserver,
|
||||
nsIChannelEventSink,
|
||||
nsIFTPChannelParentInternal)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelParent::PFTPChannelParent
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelParent methods
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
FTPChannelParent::Init(const FTPChannelCreationArgs& aArgs)
|
||||
{
|
||||
switch (aArgs.type()) {
|
||||
case FTPChannelCreationArgs::TFTPChannelOpenArgs:
|
||||
{
|
||||
const FTPChannelOpenArgs& a = aArgs.get_FTPChannelOpenArgs();
|
||||
return DoAsyncOpen(a.uri(), a.startPos(), a.entityID(), a.uploadStream(),
|
||||
a.loadInfo());
|
||||
}
|
||||
case FTPChannelCreationArgs::TFTPChannelConnectArgs:
|
||||
{
|
||||
const FTPChannelConnectArgs& cArgs = aArgs.get_FTPChannelConnectArgs();
|
||||
return ConnectChannel(cArgs.channelId());
|
||||
}
|
||||
default:
|
||||
NS_NOTREACHED("unknown open type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
FTPChannelParent::DoAsyncOpen(const URIParams& aURI,
|
||||
const uint64_t& aStartPos,
|
||||
const nsCString& aEntityID,
|
||||
const OptionalInputStreamParams& aUploadStream,
|
||||
const OptionalLoadInfoArgs& aLoadInfoArgs)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
|
||||
if (!uri)
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
LOG(("FTPChannelParent DoAsyncOpen [this=%p uri=%s]\n",
|
||||
this, uri->GetSpecOrDefault().get()));
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
return SendFailedAsyncOpen(rv);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo;
|
||||
rv = mozilla::ipc::LoadInfoArgsToLoadInfo(aLoadInfoArgs,
|
||||
getter_AddRefs(loadInfo));
|
||||
if (NS_FAILED(rv)) {
|
||||
return SendFailedAsyncOpen(rv);
|
||||
}
|
||||
|
||||
NeckoOriginAttributes attrs;
|
||||
rv = loadInfo->GetOriginAttributes(&attrs);
|
||||
if (NS_FAILED(rv)) {
|
||||
return SendFailedAsyncOpen(rv);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIChannel> chan;
|
||||
rv = NS_NewChannelInternal(getter_AddRefs(chan), uri, loadInfo,
|
||||
nullptr, nullptr,
|
||||
nsIRequest::LOAD_NORMAL, ios);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return SendFailedAsyncOpen(rv);
|
||||
|
||||
mChannel = chan;
|
||||
|
||||
// later on mChannel may become an HTTP channel (we'll be redirected to one
|
||||
// if we're using a proxy), but for now this is safe
|
||||
nsFtpChannel* ftpChan = static_cast<nsFtpChannel*>(mChannel.get());
|
||||
|
||||
if (mPBOverride != kPBOverride_Unset) {
|
||||
ftpChan->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
|
||||
}
|
||||
rv = ftpChan->SetNotificationCallbacks(this);
|
||||
if (NS_FAILED(rv))
|
||||
return SendFailedAsyncOpen(rv);
|
||||
|
||||
nsTArray<mozilla::ipc::FileDescriptor> fds;
|
||||
nsCOMPtr<nsIInputStream> upload = DeserializeInputStream(aUploadStream, fds);
|
||||
if (upload) {
|
||||
// contentType and contentLength are ignored
|
||||
rv = ftpChan->SetUploadStream(upload, EmptyCString(), 0);
|
||||
if (NS_FAILED(rv))
|
||||
return SendFailedAsyncOpen(rv);
|
||||
}
|
||||
|
||||
rv = ftpChan->ResumeAt(aStartPos, aEntityID);
|
||||
if (NS_FAILED(rv))
|
||||
return SendFailedAsyncOpen(rv);
|
||||
|
||||
if (loadInfo && loadInfo->GetEnforceSecurity()) {
|
||||
rv = ftpChan->AsyncOpen2(this);
|
||||
}
|
||||
else {
|
||||
rv = ftpChan->AsyncOpen(this, nullptr);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return SendFailedAsyncOpen(rv);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FTPChannelParent::ConnectChannel(const uint32_t& channelId)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
LOG(("Looking for a registered channel [this=%p, id=%d]", this, channelId));
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel));
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mChannel = channel;
|
||||
|
||||
LOG((" found channel %p, rv=%08x", mChannel.get(), rv));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FTPChannelParent::RecvCancel(const nsresult& status)
|
||||
{
|
||||
if (mChannel)
|
||||
mChannel->Cancel(status);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FTPChannelParent::RecvSuspend()
|
||||
{
|
||||
if (mChannel) {
|
||||
SuspendChannel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FTPChannelParent::RecvResume()
|
||||
{
|
||||
if (mChannel) {
|
||||
ResumeChannel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class FTPDivertDataAvailableEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
FTPDivertDataAvailableEvent(FTPChannelParent* aParent,
|
||||
const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count)
|
||||
: mParent(aParent)
|
||||
, mData(data)
|
||||
, mOffset(offset)
|
||||
, mCount(count)
|
||||
{
|
||||
}
|
||||
|
||||
void Run()
|
||||
{
|
||||
mParent->DivertOnDataAvailable(mData, mOffset, mCount);
|
||||
}
|
||||
|
||||
private:
|
||||
FTPChannelParent* mParent;
|
||||
nsCString mData;
|
||||
uint64_t mOffset;
|
||||
uint32_t mCount;
|
||||
};
|
||||
|
||||
bool
|
||||
FTPChannelParent::RecvDivertOnDataAvailable(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count)
|
||||
{
|
||||
if (NS_WARN_IF(!mDivertingFromChild)) {
|
||||
MOZ_ASSERT(mDivertingFromChild,
|
||||
"Cannot RecvDivertOnDataAvailable if diverting is not set!");
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Drop OnDataAvailables if the parent was canceled already.
|
||||
if (NS_FAILED(mStatus)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mEventQ->RunOrEnqueue(new FTPDivertDataAvailableEvent(this, data, offset,
|
||||
count));
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelParent::DivertOnDataAvailable(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count)
|
||||
{
|
||||
LOG(("FTPChannelParent::DivertOnDataAvailable [this=%p]\n", this));
|
||||
|
||||
if (NS_WARN_IF(!mDivertingFromChild)) {
|
||||
MOZ_ASSERT(mDivertingFromChild,
|
||||
"Cannot DivertOnDataAvailable if diverting is not set!");
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
// Drop OnDataAvailables if the parent was canceled already.
|
||||
if (NS_FAILED(mStatus)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stringStream;
|
||||
nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(),
|
||||
count, NS_ASSIGNMENT_DEPEND);
|
||||
if (NS_FAILED(rv)) {
|
||||
if (mChannel) {
|
||||
mChannel->Cancel(rv);
|
||||
}
|
||||
mStatus = rv;
|
||||
return;
|
||||
}
|
||||
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
|
||||
rv = OnDataAvailable(mChannel, nullptr, stringStream, offset, count);
|
||||
|
||||
stringStream->Close();
|
||||
if (NS_FAILED(rv)) {
|
||||
if (mChannel) {
|
||||
mChannel->Cancel(rv);
|
||||
}
|
||||
mStatus = rv;
|
||||
}
|
||||
}
|
||||
|
||||
class FTPDivertStopRequestEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
FTPDivertStopRequestEvent(FTPChannelParent* aParent,
|
||||
const nsresult& statusCode)
|
||||
: mParent(aParent)
|
||||
, mStatusCode(statusCode)
|
||||
{
|
||||
}
|
||||
|
||||
void Run() {
|
||||
mParent->DivertOnStopRequest(mStatusCode);
|
||||
}
|
||||
|
||||
private:
|
||||
FTPChannelParent* mParent;
|
||||
nsresult mStatusCode;
|
||||
};
|
||||
|
||||
bool
|
||||
FTPChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode)
|
||||
{
|
||||
if (NS_WARN_IF(!mDivertingFromChild)) {
|
||||
MOZ_ASSERT(mDivertingFromChild,
|
||||
"Cannot RecvDivertOnStopRequest if diverting is not set!");
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
return false;
|
||||
}
|
||||
|
||||
mEventQ->RunOrEnqueue(new FTPDivertStopRequestEvent(this, statusCode));
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelParent::DivertOnStopRequest(const nsresult& statusCode)
|
||||
{
|
||||
LOG(("FTPChannelParent::DivertOnStopRequest [this=%p]\n", this));
|
||||
|
||||
if (NS_WARN_IF(!mDivertingFromChild)) {
|
||||
MOZ_ASSERT(mDivertingFromChild,
|
||||
"Cannot DivertOnStopRequest if diverting is not set!");
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
// Honor the channel's status even if the underlying transaction completed.
|
||||
nsresult status = NS_FAILED(mStatus) ? mStatus : statusCode;
|
||||
|
||||
// Reset fake pending status in case OnStopRequest has already been called.
|
||||
if (mChannel) {
|
||||
nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
|
||||
if (forcePendingIChan) {
|
||||
forcePendingIChan->ForcePending(false);
|
||||
}
|
||||
}
|
||||
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
OnStopRequest(mChannel, nullptr, status);
|
||||
}
|
||||
|
||||
class FTPDivertCompleteEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
explicit FTPDivertCompleteEvent(FTPChannelParent* aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
}
|
||||
|
||||
void Run() {
|
||||
mParent->DivertComplete();
|
||||
}
|
||||
|
||||
private:
|
||||
FTPChannelParent* mParent;
|
||||
};
|
||||
|
||||
bool
|
||||
FTPChannelParent::RecvDivertComplete()
|
||||
{
|
||||
if (NS_WARN_IF(!mDivertingFromChild)) {
|
||||
MOZ_ASSERT(mDivertingFromChild,
|
||||
"Cannot RecvDivertComplete if diverting is not set!");
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
return false;
|
||||
}
|
||||
|
||||
mEventQ->RunOrEnqueue(new FTPDivertCompleteEvent(this));
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelParent::DivertComplete()
|
||||
{
|
||||
LOG(("FTPChannelParent::DivertComplete [this=%p]\n", this));
|
||||
|
||||
if (NS_WARN_IF(!mDivertingFromChild)) {
|
||||
MOZ_ASSERT(mDivertingFromChild,
|
||||
"Cannot DivertComplete if diverting is not set!");
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = ResumeForDiversion();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelParent::nsIRequestObserver
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelParent::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
||||
{
|
||||
LOG(("FTPChannelParent::OnStartRequest [this=%p]\n", this));
|
||||
|
||||
if (mDivertingFromChild) {
|
||||
MOZ_RELEASE_ASSERT(mDivertToListener,
|
||||
"Cannot divert if listener is unset!");
|
||||
return mDivertToListener->OnStartRequest(aRequest, aContext);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIChannel> chan = do_QueryInterface(aRequest);
|
||||
MOZ_ASSERT(chan);
|
||||
NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
|
||||
|
||||
int64_t contentLength;
|
||||
chan->GetContentLength(&contentLength);
|
||||
nsCString contentType;
|
||||
chan->GetContentType(contentType);
|
||||
|
||||
nsCString entityID;
|
||||
nsCOMPtr<nsIResumableChannel> resChan = do_QueryInterface(aRequest);
|
||||
MOZ_ASSERT(resChan); // both FTP and HTTP should implement nsIResumableChannel
|
||||
if (resChan) {
|
||||
resChan->GetEntityID(entityID);
|
||||
}
|
||||
|
||||
PRTime lastModified = 0;
|
||||
nsCOMPtr<nsIFTPChannel> ftpChan = do_QueryInterface(aRequest);
|
||||
if (ftpChan) {
|
||||
ftpChan->GetLastModifiedTime(&lastModified);
|
||||
}
|
||||
nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(aRequest);
|
||||
if (httpChan) {
|
||||
httpChan->GetLastModifiedTime(&lastModified);
|
||||
}
|
||||
|
||||
URIParams uriparam;
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
chan->GetURI(getter_AddRefs(uri));
|
||||
SerializeURI(uri, uriparam);
|
||||
|
||||
if (mIPCClosed || !SendOnStartRequest(mStatus, contentLength, contentType,
|
||||
lastModified, entityID, uriparam)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelParent::OnStopRequest(nsIRequest* aRequest,
|
||||
nsISupports* aContext,
|
||||
nsresult aStatusCode)
|
||||
{
|
||||
LOG(("FTPChannelParent::OnStopRequest: [this=%p status=%ul]\n",
|
||||
this, aStatusCode));
|
||||
|
||||
if (mDivertingFromChild) {
|
||||
MOZ_RELEASE_ASSERT(mDivertToListener,
|
||||
"Cannot divert if listener is unset!");
|
||||
return mDivertToListener->OnStopRequest(aRequest, aContext, aStatusCode);
|
||||
}
|
||||
|
||||
if (mIPCClosed || !SendOnStopRequest(aStatusCode, mErrorMsg, mUseUTF8)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelParent::nsIStreamListener
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelParent::OnDataAvailable(nsIRequest* aRequest,
|
||||
nsISupports* aContext,
|
||||
nsIInputStream* aInputStream,
|
||||
uint64_t aOffset,
|
||||
uint32_t aCount)
|
||||
{
|
||||
LOG(("FTPChannelParent::OnDataAvailable [this=%p]\n", this));
|
||||
|
||||
if (mDivertingFromChild) {
|
||||
MOZ_RELEASE_ASSERT(mDivertToListener,
|
||||
"Cannot divert if listener is unset!");
|
||||
return mDivertToListener->OnDataAvailable(aRequest, aContext, aInputStream,
|
||||
aOffset, aCount);
|
||||
}
|
||||
|
||||
nsCString data;
|
||||
nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (mIPCClosed || !SendOnDataAvailable(mStatus, data, aOffset, aCount))
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelParent::nsIParentChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelParent::SetParentListener(HttpChannelParentListener* aListener)
|
||||
{
|
||||
// Do not need ptr to HttpChannelParentListener.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelParent::NotifyTrackingProtectionDisabled()
|
||||
{
|
||||
// One day, this should probably be filled in.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelParent::Delete()
|
||||
{
|
||||
if (mIPCClosed || !SendDeleteSelf())
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelParent::nsIInterfaceRequestor
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelParent::GetInterface(const nsIID& uuid, void** result)
|
||||
{
|
||||
if (uuid.Equals(NS_GET_IID(nsIAuthPromptProvider)) ||
|
||||
uuid.Equals(NS_GET_IID(nsISecureBrowserUI))) {
|
||||
if (mTabParent) {
|
||||
return mTabParent->QueryInterface(uuid, result);
|
||||
}
|
||||
} else if (uuid.Equals(NS_GET_IID(nsIAuthPrompt)) ||
|
||||
uuid.Equals(NS_GET_IID(nsIAuthPrompt2))) {
|
||||
nsCOMPtr<nsIAuthPromptProvider> provider(do_QueryObject(mTabParent));
|
||||
if (provider) {
|
||||
return provider->GetAuthPrompt(nsIAuthPromptProvider::PROMPT_NORMAL,
|
||||
uuid,
|
||||
result);
|
||||
}
|
||||
}
|
||||
|
||||
// Only support nsILoadContext if child channel's callbacks did too
|
||||
if (uuid.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
|
||||
nsCOMPtr<nsILoadContext> copy = mLoadContext;
|
||||
copy.forget(result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return QueryInterface(uuid, result);
|
||||
}
|
||||
|
||||
nsresult
|
||||
FTPChannelParent::SuspendChannel()
|
||||
{
|
||||
nsCOMPtr<nsIChannelWithDivertableParentListener> chan =
|
||||
do_QueryInterface(mChannel);
|
||||
if (chan) {
|
||||
return chan->SuspendInternal();
|
||||
} else {
|
||||
return mChannel->Suspend();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
FTPChannelParent::ResumeChannel()
|
||||
{
|
||||
nsCOMPtr<nsIChannelWithDivertableParentListener> chan =
|
||||
do_QueryInterface(mChannel);
|
||||
if (chan) {
|
||||
return chan->ResumeInternal();
|
||||
} else {
|
||||
return mChannel->Resume();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelParent::ADivertableParentChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
nsresult
|
||||
FTPChannelParent::SuspendForDiversion()
|
||||
{
|
||||
MOZ_ASSERT(mChannel);
|
||||
if (NS_WARN_IF(mDivertingFromChild)) {
|
||||
MOZ_ASSERT(!mDivertingFromChild, "Already suspended for diversion!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Try suspending the channel. Allow it to fail, since OnStopRequest may have
|
||||
// been called and thus the channel may not be pending.
|
||||
nsresult rv = SuspendChannel();
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_AVAILABLE);
|
||||
mSuspendedForDiversion = NS_SUCCEEDED(rv);
|
||||
|
||||
// Once this is set, no more OnStart/OnData/OnStop callbacks should be sent
|
||||
// to the child.
|
||||
mDivertingFromChild = true;
|
||||
|
||||
nsCOMPtr<nsIChannelWithDivertableParentListener> chan =
|
||||
do_QueryInterface(mChannel);
|
||||
if (chan) {
|
||||
chan->MessageDiversionStarted(this);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* private, supporting function for ADivertableParentChannel */
|
||||
nsresult
|
||||
FTPChannelParent::ResumeForDiversion()
|
||||
{
|
||||
MOZ_ASSERT(mChannel);
|
||||
MOZ_ASSERT(mDivertToListener);
|
||||
if (NS_WARN_IF(!mDivertingFromChild)) {
|
||||
MOZ_ASSERT(mDivertingFromChild,
|
||||
"Cannot ResumeForDiversion if not diverting!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIChannelWithDivertableParentListener> chan =
|
||||
do_QueryInterface(mChannel);
|
||||
if (chan) {
|
||||
chan->MessageDiversionStop();
|
||||
}
|
||||
|
||||
if (mSuspendedForDiversion) {
|
||||
nsresult rv = ResumeChannel();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailDiversion(NS_ERROR_UNEXPECTED, true);
|
||||
return rv;
|
||||
}
|
||||
mSuspendedForDiversion = false;
|
||||
}
|
||||
|
||||
// Delete() will tear down IPDL, but ref from underlying nsFTPChannel will
|
||||
// keep us alive if there's more data to be delivered to listener.
|
||||
if (NS_WARN_IF(NS_FAILED(Delete()))) {
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FTPChannelParent::SuspendMessageDiversion()
|
||||
{
|
||||
// This only need to suspend message queue.
|
||||
mEventQ->Suspend();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FTPChannelParent::ResumeMessageDiversion()
|
||||
{
|
||||
// This only need to resumes message queue.
|
||||
mEventQ->Resume();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelParent::DivertTo(nsIStreamListener *aListener)
|
||||
{
|
||||
MOZ_ASSERT(aListener);
|
||||
if (NS_WARN_IF(!mDivertingFromChild)) {
|
||||
MOZ_ASSERT(mDivertingFromChild,
|
||||
"Cannot DivertTo new listener if diverting is not set!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(mIPCClosed || !SendFlushedForDiversion())) {
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
mDivertToListener = aListener;
|
||||
|
||||
// Call OnStartRequest and SendDivertMessages asynchronously to avoid
|
||||
// reentering client context.
|
||||
NS_DispatchToCurrentThread(
|
||||
NewRunnableMethod(this, &FTPChannelParent::StartDiversion));
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelParent::StartDiversion()
|
||||
{
|
||||
if (NS_WARN_IF(!mDivertingFromChild)) {
|
||||
MOZ_ASSERT(mDivertingFromChild,
|
||||
"Cannot StartDiversion if diverting is not set!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fake pending status in case OnStopRequest has already been called.
|
||||
if (mChannel) {
|
||||
nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
|
||||
if (forcePendingIChan) {
|
||||
forcePendingIChan->ForcePending(true);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
// Call OnStartRequest for the "DivertTo" listener.
|
||||
nsresult rv = OnStartRequest(mChannel, nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
if (mChannel) {
|
||||
mChannel->Cancel(rv);
|
||||
}
|
||||
mStatus = rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// After OnStartRequest has been called, tell FTPChannelChild to divert the
|
||||
// OnDataAvailables and OnStopRequest to this FTPChannelParent.
|
||||
if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) {
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
class FTPFailDiversionEvent : public Runnable
|
||||
{
|
||||
public:
|
||||
FTPFailDiversionEvent(FTPChannelParent *aChannelParent,
|
||||
nsresult aErrorCode,
|
||||
bool aSkipResume)
|
||||
: mChannelParent(aChannelParent)
|
||||
, mErrorCode(aErrorCode)
|
||||
, mSkipResume(aSkipResume)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aChannelParent);
|
||||
MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
|
||||
}
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mChannelParent->NotifyDiversionFailed(mErrorCode, mSkipResume);
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
RefPtr<FTPChannelParent> mChannelParent;
|
||||
nsresult mErrorCode;
|
||||
bool mSkipResume;
|
||||
};
|
||||
|
||||
void
|
||||
FTPChannelParent::FailDiversion(nsresult aErrorCode,
|
||||
bool aSkipResume)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
|
||||
MOZ_RELEASE_ASSERT(mDivertingFromChild);
|
||||
MOZ_RELEASE_ASSERT(mDivertToListener);
|
||||
MOZ_RELEASE_ASSERT(mChannel);
|
||||
|
||||
NS_DispatchToCurrentThread(
|
||||
new FTPFailDiversionEvent(this, aErrorCode, aSkipResume));
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelParent::NotifyDiversionFailed(nsresult aErrorCode,
|
||||
bool aSkipResume)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
|
||||
MOZ_RELEASE_ASSERT(mDivertingFromChild);
|
||||
MOZ_RELEASE_ASSERT(mDivertToListener);
|
||||
MOZ_RELEASE_ASSERT(mChannel);
|
||||
|
||||
mChannel->Cancel(aErrorCode);
|
||||
nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
|
||||
if (forcePendingIChan) {
|
||||
forcePendingIChan->ForcePending(false);
|
||||
}
|
||||
|
||||
bool isPending = false;
|
||||
nsresult rv = mChannel->IsPending(&isPending);
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
// Resume only we suspended earlier.
|
||||
if (mSuspendedForDiversion) {
|
||||
ResumeChannel();
|
||||
}
|
||||
// Channel has already sent OnStartRequest to the child, so ensure that we
|
||||
// call it here if it hasn't already been called.
|
||||
if (!mDivertedOnStartRequest) {
|
||||
nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
|
||||
if (forcePendingIChan) {
|
||||
forcePendingIChan->ForcePending(true);
|
||||
}
|
||||
mDivertToListener->OnStartRequest(mChannel, nullptr);
|
||||
|
||||
if (forcePendingIChan) {
|
||||
forcePendingIChan->ForcePending(false);
|
||||
}
|
||||
}
|
||||
// If the channel is pending, it will call OnStopRequest itself; otherwise, do
|
||||
// it here.
|
||||
if (!isPending) {
|
||||
mDivertToListener->OnStopRequest(mChannel, nullptr, aErrorCode);
|
||||
}
|
||||
mDivertToListener = nullptr;
|
||||
mChannel = nullptr;
|
||||
|
||||
if (!mIPCClosed) {
|
||||
Unused << SendDeleteSelf();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelParent::nsIChannelEventSink
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelParent::AsyncOnChannelRedirect(
|
||||
nsIChannel *oldChannel,
|
||||
nsIChannel *newChannel,
|
||||
uint32_t redirectFlags,
|
||||
nsIAsyncVerifyRedirectCallback* callback)
|
||||
{
|
||||
nsCOMPtr<nsIFTPChannel> ftpChan = do_QueryInterface(newChannel);
|
||||
if (!ftpChan) {
|
||||
// when FTP is set to use HTTP proxying, we wind up getting redirected to an HTTP channel.
|
||||
nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(newChannel);
|
||||
if (!httpChan)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
mChannel = newChannel;
|
||||
callback->OnRedirectVerifyCallback(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelParent::SetErrorMsg(const char *aMsg, bool aUseUTF8)
|
||||
{
|
||||
mErrorMsg = aMsg;
|
||||
mUseUTF8 = aUseUTF8;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//---------------------
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
146
netwerk/protocol/ftp/FTPChannelParent.h
Normal file
146
netwerk/protocol/ftp/FTPChannelParent.h
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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_FTPChannelParent_h
|
||||
#define mozilla_net_FTPChannelParent_h
|
||||
|
||||
#include "ADivertableParentChannel.h"
|
||||
#include "mozilla/net/PFTPChannelParent.h"
|
||||
#include "mozilla/net/NeckoParent.h"
|
||||
#include "nsIParentChannel.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
#include "nsIFTPChannelParentInternal.h"
|
||||
|
||||
class nsILoadContext;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class TabParent;
|
||||
class PBrowserOrId;
|
||||
} // namespace dom
|
||||
|
||||
namespace net {
|
||||
class ChannelEventQueue;
|
||||
|
||||
class FTPChannelParent final : public PFTPChannelParent
|
||||
, public nsIParentChannel
|
||||
, public nsIInterfaceRequestor
|
||||
, public ADivertableParentChannel
|
||||
, public nsIChannelEventSink
|
||||
, public nsIFTPChannelParentInternal
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIPARENTCHANNEL
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
|
||||
FTPChannelParent(const dom::PBrowserOrId& aIframeEmbedding,
|
||||
nsILoadContext* aLoadContext,
|
||||
PBOverrideStatus aOverrideStatus);
|
||||
|
||||
bool Init(const FTPChannelCreationArgs& aOpenArgs);
|
||||
|
||||
// ADivertableParentChannel functions.
|
||||
void DivertTo(nsIStreamListener *aListener) override;
|
||||
nsresult SuspendForDiversion() override;
|
||||
nsresult SuspendMessageDiversion() override;
|
||||
nsresult ResumeMessageDiversion() override;
|
||||
|
||||
// Calls OnStartRequest for "DivertTo" listener, then notifies child channel
|
||||
// that it should divert OnDataAvailable and OnStopRequest calls to this
|
||||
// parent channel.
|
||||
void StartDiversion();
|
||||
|
||||
// Handles calling OnStart/Stop if there are errors during diversion.
|
||||
// Called asynchronously from FailDiversion.
|
||||
void NotifyDiversionFailed(nsresult aErrorCode, bool aSkipResume = true);
|
||||
|
||||
NS_IMETHOD SetErrorMsg(const char *aMsg, bool aUseUTF8) override;
|
||||
|
||||
protected:
|
||||
virtual ~FTPChannelParent();
|
||||
|
||||
// private, supporting function for ADivertableParentChannel.
|
||||
nsresult ResumeForDiversion();
|
||||
|
||||
// Asynchronously calls NotifyDiversionFailed.
|
||||
void FailDiversion(nsresult aErrorCode, bool aSkipResume = true);
|
||||
|
||||
bool DoAsyncOpen(const URIParams& aURI, const uint64_t& aStartPos,
|
||||
const nsCString& aEntityID,
|
||||
const OptionalInputStreamParams& aUploadStream,
|
||||
const OptionalLoadInfoArgs& aLoadInfoArgs);
|
||||
|
||||
// used to connect redirected-to channel in parent with just created
|
||||
// ChildChannel. Used during HTTP->FTP redirects.
|
||||
bool ConnectChannel(const uint32_t& channelId);
|
||||
|
||||
void DivertOnDataAvailable(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count);
|
||||
void DivertOnStopRequest(const nsresult& statusCode);
|
||||
void DivertComplete();
|
||||
|
||||
friend class FTPDivertDataAvailableEvent;
|
||||
friend class FTPDivertStopRequestEvent;
|
||||
friend class FTPDivertCompleteEvent;
|
||||
|
||||
virtual bool RecvCancel(const nsresult& status) override;
|
||||
virtual bool RecvSuspend() override;
|
||||
virtual bool RecvResume() override;
|
||||
virtual bool RecvDivertOnDataAvailable(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count) override;
|
||||
virtual bool RecvDivertOnStopRequest(const nsresult& statusCode) override;
|
||||
virtual bool RecvDivertComplete() override;
|
||||
|
||||
nsresult SuspendChannel();
|
||||
nsresult ResumeChannel();
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
|
||||
// if configured to use HTTP proxy for FTP, this can an an HTTP channel.
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
|
||||
bool mIPCClosed;
|
||||
|
||||
nsCOMPtr<nsILoadContext> mLoadContext;
|
||||
|
||||
PBOverrideStatus mPBOverride;
|
||||
|
||||
// If OnStart/OnData/OnStop have been diverted from the child, forward them to
|
||||
// this listener.
|
||||
nsCOMPtr<nsIStreamListener> mDivertToListener;
|
||||
// Set to the canceled status value if the main channel was canceled.
|
||||
nsresult mStatus;
|
||||
// Once set, no OnStart/OnData/OnStop calls should be accepted; conversely, it
|
||||
// must be set when RecvDivertOnData/~DivertOnStop/~DivertComplete are
|
||||
// received from the child channel.
|
||||
bool mDivertingFromChild;
|
||||
// Set if OnStart|StopRequest was called during a diversion from the child.
|
||||
bool mDivertedOnStartRequest;
|
||||
|
||||
// Set if we successfully suspended the nsHttpChannel for diversion. Unset
|
||||
// when we call ResumeForDiversion.
|
||||
bool mSuspendedForDiversion;
|
||||
RefPtr<mozilla::dom::TabParent> mTabParent;
|
||||
|
||||
RefPtr<ChannelEventQueue> mEventQ;
|
||||
|
||||
nsCString mErrorMsg;
|
||||
bool mUseUTF8;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_FTPChannelParent_h
|
||||
74
netwerk/protocol/ftp/PFTPChannel.ipdl
Normal file
74
netwerk/protocol/ftp/PFTPChannel.ipdl
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
|
||||
|
||||
/* 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/. */
|
||||
|
||||
include protocol PNecko;
|
||||
include InputStreamParams;
|
||||
include URIParams;
|
||||
|
||||
//FIXME: bug #792908 (NeckoChannelParams already included by PNecko)
|
||||
include NeckoChannelParams;
|
||||
include protocol PBlob; //FIXME: bug #792908
|
||||
|
||||
using PRTime from "prtime.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
async protocol PFTPChannel
|
||||
{
|
||||
manager PNecko;
|
||||
|
||||
parent:
|
||||
// Note: channels are opened during construction, so no open method here:
|
||||
// see PNecko.ipdl
|
||||
|
||||
async __delete__();
|
||||
|
||||
async Cancel(nsresult status);
|
||||
async Suspend();
|
||||
async Resume();
|
||||
|
||||
// Divert OnDataAvailable to the parent.
|
||||
async DivertOnDataAvailable(nsCString data,
|
||||
uint64_t offset,
|
||||
uint32_t count);
|
||||
|
||||
// Divert OnStopRequest to the parent.
|
||||
async DivertOnStopRequest(nsresult statusCode);
|
||||
|
||||
// Child has no more events/messages to divert to the parent.
|
||||
async DivertComplete();
|
||||
|
||||
child:
|
||||
async OnStartRequest(nsresult aChannelStatus,
|
||||
int64_t aContentLength,
|
||||
nsCString aContentType,
|
||||
PRTime aLastModified,
|
||||
nsCString aEntityID,
|
||||
URIParams aURI);
|
||||
async OnDataAvailable(nsresult channelStatus,
|
||||
nsCString data,
|
||||
uint64_t offset,
|
||||
uint32_t count);
|
||||
async OnStopRequest(nsresult channelStatus,
|
||||
nsCString aErrorMsg,
|
||||
bool aUseUTF8);
|
||||
async FailedAsyncOpen(nsresult statusCode);
|
||||
|
||||
// Parent has been suspended for diversion; no more events to be enqueued.
|
||||
async FlushedForDiversion();
|
||||
|
||||
// Child should resume processing the ChannelEventQueue, i.e. diverting any
|
||||
// OnDataAvailable and OnStopRequest messages in the queue back to the parent.
|
||||
async DivertMessages();
|
||||
|
||||
async DeleteSelf();
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
3933
netwerk/protocol/ftp/doc/rfc959.txt
Normal file
3933
netwerk/protocol/ftp/doc/rfc959.txt
Normal file
File diff suppressed because it is too large
Load diff
4
netwerk/protocol/ftp/doc/testdoc
Normal file
4
netwerk/protocol/ftp/doc/testdoc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
Test
|
||||
here
|
||||
there
|
||||
everywhere
|
||||
15
netwerk/protocol/ftp/ftpCore.h
Normal file
15
netwerk/protocol/ftp/ftpCore.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 __ftpCore_h___
|
||||
#define __ftpCore_h___
|
||||
|
||||
#include "nsError.h"
|
||||
|
||||
/**
|
||||
* Status nsresult codes
|
||||
*/
|
||||
|
||||
#endif // __ftpCore_h___
|
||||
45
netwerk/protocol/ftp/moz.build
Normal file
45
netwerk/protocol/ftp/moz.build
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIFTPChannel.idl',
|
||||
'nsIFTPChannelParentInternal.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'necko_ftp'
|
||||
|
||||
EXPORTS += [
|
||||
'ftpCore.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.net += [
|
||||
'FTPChannelChild.h',
|
||||
'FTPChannelParent.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'FTPChannelChild.cpp',
|
||||
'FTPChannelParent.cpp',
|
||||
'nsFTPChannel.cpp',
|
||||
'nsFtpConnectionThread.cpp',
|
||||
'nsFtpControlConnection.cpp',
|
||||
'nsFtpProtocolHandler.cpp',
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'PFTPChannel.ipdl',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/netwerk/base',
|
||||
]
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
294
netwerk/protocol/ftp/nsFTPChannel.cpp
Normal file
294
netwerk/protocol/ftp/nsFTPChannel.cpp
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set ts=4 sts=4 sw=4 et cin: */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsFTPChannel.h"
|
||||
#include "nsFtpConnectionThread.h" // defines nsFtpState
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::net;
|
||||
extern LazyLogModule gFTPLog;
|
||||
|
||||
// There are two transport connections established for an
|
||||
// ftp connection. One is used for the command channel , and
|
||||
// the other for the data channel. The command channel is the first
|
||||
// connection made and is used to negotiate the second, data, channel.
|
||||
// The data channel is driven by the command channel and is either
|
||||
// initiated by the server (PORT command) or by the client (PASV command).
|
||||
// Client initiation is the most common case and is attempted first.
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(nsFtpChannel,
|
||||
nsBaseChannel,
|
||||
nsIUploadChannel,
|
||||
nsIResumableChannel,
|
||||
nsIFTPChannel,
|
||||
nsIProxiedChannel,
|
||||
nsIForcePendingChannel,
|
||||
nsIChannelWithDivertableParentListener)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpChannel::SetUploadStream(nsIInputStream *stream,
|
||||
const nsACString &contentType,
|
||||
int64_t contentLength)
|
||||
{
|
||||
NS_ENSURE_TRUE(!Pending(), NS_ERROR_IN_PROGRESS);
|
||||
|
||||
mUploadStream = stream;
|
||||
|
||||
// NOTE: contentLength is intentionally ignored here.
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpChannel::GetUploadStream(nsIInputStream **stream)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(stream);
|
||||
*stream = mUploadStream;
|
||||
NS_IF_ADDREF(*stream);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpChannel::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID)
|
||||
{
|
||||
NS_ENSURE_TRUE(!Pending(), NS_ERROR_IN_PROGRESS);
|
||||
mEntityID = aEntityID;
|
||||
mStartPos = aStartPos;
|
||||
mResumeRequested = (mStartPos || !mEntityID.IsEmpty());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpChannel::GetEntityID(nsACString& entityID)
|
||||
{
|
||||
if (mEntityID.IsEmpty())
|
||||
return NS_ERROR_NOT_RESUMABLE;
|
||||
|
||||
entityID = mEntityID;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
NS_IMETHODIMP
|
||||
nsFtpChannel::GetProxyInfo(nsIProxyInfo** aProxyInfo)
|
||||
{
|
||||
*aProxyInfo = ProxyInfo();
|
||||
NS_IF_ADDREF(*aProxyInfo);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
nsFtpChannel::OpenContentStream(bool async, nsIInputStream **result,
|
||||
nsIChannel** channel)
|
||||
{
|
||||
if (!async)
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
nsFtpState *state = new nsFtpState();
|
||||
if (!state)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(state);
|
||||
|
||||
nsresult rv = state->Init(this);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_RELEASE(state);
|
||||
return rv;
|
||||
}
|
||||
|
||||
*result = state;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsFtpChannel::GetStatusArg(nsresult status, nsString &statusArg)
|
||||
{
|
||||
nsAutoCString host;
|
||||
URI()->GetHost(host);
|
||||
CopyUTF8toUTF16(host, statusArg);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsFtpChannel::OnCallbacksChanged()
|
||||
{
|
||||
mFTPEventSink = nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
||||
class FTPEventSinkProxy final : public nsIFTPEventSink
|
||||
{
|
||||
~FTPEventSinkProxy() {}
|
||||
|
||||
public:
|
||||
explicit FTPEventSinkProxy(nsIFTPEventSink* aTarget)
|
||||
: mTarget(aTarget)
|
||||
, mTargetThread(do_GetCurrentThread())
|
||||
{ }
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIFTPEVENTSINK
|
||||
|
||||
class OnFTPControlLogRunnable : public Runnable
|
||||
{
|
||||
public:
|
||||
OnFTPControlLogRunnable(nsIFTPEventSink* aTarget,
|
||||
bool aServer,
|
||||
const char* aMessage)
|
||||
: mTarget(aTarget)
|
||||
, mServer(aServer)
|
||||
, mMessage(aMessage)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIFTPEventSink> mTarget;
|
||||
bool mServer;
|
||||
nsCString mMessage;
|
||||
};
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIFTPEventSink> mTarget;
|
||||
nsCOMPtr<nsIThread> mTargetThread;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(FTPEventSinkProxy, nsIFTPEventSink)
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPEventSinkProxy::OnFTPControlLog(bool aServer, const char* aMsg)
|
||||
{
|
||||
RefPtr<OnFTPControlLogRunnable> r =
|
||||
new OnFTPControlLogRunnable(mTarget, aServer, aMsg);
|
||||
return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPEventSinkProxy::OnFTPControlLogRunnable::Run()
|
||||
{
|
||||
mTarget->OnFTPControlLog(mServer, mMessage.get());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void
|
||||
nsFtpChannel::GetFTPEventSink(nsCOMPtr<nsIFTPEventSink> &aResult)
|
||||
{
|
||||
if (!mFTPEventSink) {
|
||||
nsCOMPtr<nsIFTPEventSink> ftpSink;
|
||||
GetCallback(ftpSink);
|
||||
if (ftpSink) {
|
||||
mFTPEventSink = new FTPEventSinkProxy(ftpSink);
|
||||
}
|
||||
}
|
||||
aResult = mFTPEventSink;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpChannel::ForcePending(bool aForcePending)
|
||||
{
|
||||
// Set true here so IsPending will return true.
|
||||
// Required for callback diversion from child back to parent. In such cases
|
||||
// OnStopRequest can be called in the parent before callbacks are diverted
|
||||
// back from the child to the listener in the parent.
|
||||
mForcePending = aForcePending;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpChannel::IsPending(bool *result)
|
||||
{
|
||||
*result = Pending();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsFtpChannel::Pending() const
|
||||
{
|
||||
return nsBaseChannel::Pending() || mForcePending;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpChannel::Suspend()
|
||||
{
|
||||
LOG(("nsFtpChannel::Suspend [this=%p]\n", this));
|
||||
|
||||
nsresult rv = nsBaseChannel::Suspend();
|
||||
|
||||
nsresult rvParentChannel = NS_OK;
|
||||
if (mParentChannel) {
|
||||
rvParentChannel = mParentChannel->SuspendMessageDiversion();
|
||||
}
|
||||
|
||||
return NS_FAILED(rv) ? rv : rvParentChannel;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpChannel::Resume()
|
||||
{
|
||||
LOG(("nsFtpChannel::Resume [this=%p]\n", this));
|
||||
|
||||
nsresult rv = nsBaseChannel::Resume();
|
||||
|
||||
nsresult rvParentChannel = NS_OK;
|
||||
if (mParentChannel) {
|
||||
rvParentChannel = mParentChannel->ResumeMessageDiversion();
|
||||
}
|
||||
|
||||
return NS_FAILED(rv) ? rv : rvParentChannel;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// AChannelHasDivertableParentChannelAsListener internal functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpChannel::MessageDiversionStarted(ADivertableParentChannel *aParentChannel)
|
||||
{
|
||||
MOZ_ASSERT(!mParentChannel);
|
||||
mParentChannel = aParentChannel;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpChannel::MessageDiversionStop()
|
||||
{
|
||||
LOG(("nsFtpChannel::MessageDiversionStop [this=%p]", this));
|
||||
MOZ_ASSERT(mParentChannel);
|
||||
mParentChannel = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpChannel::SuspendInternal()
|
||||
{
|
||||
LOG(("nsFtpChannel::SuspendInternal [this=%p]\n", this));
|
||||
|
||||
return nsBaseChannel::Suspend();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpChannel::ResumeInternal()
|
||||
{
|
||||
LOG(("nsFtpChannel::ResumeInternal [this=%p]\n", this));
|
||||
|
||||
return nsBaseChannel::Resume();
|
||||
}
|
||||
122
netwerk/protocol/ftp/nsFTPChannel.h
Normal file
122
netwerk/protocol/ftp/nsFTPChannel.h
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set ts=4 sw=4 sts=4 et cindent: */
|
||||
/* 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 nsFTPChannel_h___
|
||||
#define nsFTPChannel_h___
|
||||
|
||||
#include "nsBaseChannel.h"
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIChannelWithDivertableParentListener.h"
|
||||
#include "nsIFTPChannel.h"
|
||||
#include "nsIForcePendingChannel.h"
|
||||
#include "nsIUploadChannel.h"
|
||||
#include "nsIProxyInfo.h"
|
||||
#include "nsIProxiedChannel.h"
|
||||
#include "nsIResumableChannel.h"
|
||||
|
||||
class nsIURI;
|
||||
using mozilla::net::ADivertableParentChannel;
|
||||
|
||||
class nsFtpChannel final : public nsBaseChannel,
|
||||
public nsIFTPChannel,
|
||||
public nsIUploadChannel,
|
||||
public nsIResumableChannel,
|
||||
public nsIProxiedChannel,
|
||||
public nsIForcePendingChannel,
|
||||
public nsIChannelWithDivertableParentListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIUPLOADCHANNEL
|
||||
NS_DECL_NSIRESUMABLECHANNEL
|
||||
NS_DECL_NSIPROXIEDCHANNEL
|
||||
NS_DECL_NSICHANNELWITHDIVERTABLEPARENTLISTENER
|
||||
|
||||
nsFtpChannel(nsIURI *uri, nsIProxyInfo *pi)
|
||||
: mProxyInfo(pi)
|
||||
, mStartPos(0)
|
||||
, mResumeRequested(false)
|
||||
, mLastModifiedTime(0)
|
||||
, mForcePending(false)
|
||||
{
|
||||
SetURI(uri);
|
||||
}
|
||||
|
||||
nsIProxyInfo *ProxyInfo() {
|
||||
return mProxyInfo;
|
||||
}
|
||||
|
||||
void SetProxyInfo(nsIProxyInfo *pi)
|
||||
{
|
||||
mProxyInfo = pi;
|
||||
}
|
||||
|
||||
NS_IMETHOD IsPending(bool *result) override;
|
||||
|
||||
// This is a short-cut to calling nsIRequest::IsPending().
|
||||
// Overrides Pending in nsBaseChannel.
|
||||
bool Pending() const override;
|
||||
|
||||
// Were we asked to resume a download?
|
||||
bool ResumeRequested() { return mResumeRequested; }
|
||||
|
||||
// Download from this byte offset
|
||||
uint64_t StartPos() { return mStartPos; }
|
||||
|
||||
// ID of the entity to resume downloading
|
||||
const nsCString &EntityID() {
|
||||
return mEntityID;
|
||||
}
|
||||
void SetEntityID(const nsCSubstring &entityID) {
|
||||
mEntityID = entityID;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetLastModifiedTime(PRTime* lastModifiedTime) override {
|
||||
*lastModifiedTime = mLastModifiedTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD SetLastModifiedTime(PRTime lastModifiedTime) override {
|
||||
mLastModifiedTime = lastModifiedTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Data stream to upload
|
||||
nsIInputStream *UploadStream() {
|
||||
return mUploadStream;
|
||||
}
|
||||
|
||||
// Helper function for getting the nsIFTPEventSink.
|
||||
void GetFTPEventSink(nsCOMPtr<nsIFTPEventSink> &aResult);
|
||||
|
||||
NS_IMETHOD Suspend() override;
|
||||
NS_IMETHOD Resume() override;
|
||||
|
||||
public:
|
||||
NS_IMETHOD ForcePending(bool aForcePending) override;
|
||||
|
||||
protected:
|
||||
virtual ~nsFtpChannel() {}
|
||||
virtual nsresult OpenContentStream(bool async, nsIInputStream **result,
|
||||
nsIChannel** channel) override;
|
||||
virtual bool GetStatusArg(nsresult status, nsString &statusArg) override;
|
||||
virtual void OnCallbacksChanged() override;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIProxyInfo> mProxyInfo;
|
||||
nsCOMPtr<nsIFTPEventSink> mFTPEventSink;
|
||||
nsCOMPtr<nsIInputStream> mUploadStream;
|
||||
uint64_t mStartPos;
|
||||
nsCString mEntityID;
|
||||
bool mResumeRequested;
|
||||
PRTime mLastModifiedTime;
|
||||
bool mForcePending;
|
||||
RefPtr<ADivertableParentChannel> mParentChannel;
|
||||
};
|
||||
|
||||
#endif /* nsFTPChannel_h___ */
|
||||
2256
netwerk/protocol/ftp/nsFtpConnectionThread.cpp
Normal file
2256
netwerk/protocol/ftp/nsFtpConnectionThread.cpp
Normal file
File diff suppressed because it is too large
Load diff
231
netwerk/protocol/ftp/nsFtpConnectionThread.h
Normal file
231
netwerk/protocol/ftp/nsFtpConnectionThread.h
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 __nsFtpConnectionThread__h_
|
||||
#define __nsFtpConnectionThread__h_
|
||||
|
||||
#include "nsBaseContentStream.h"
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsITransport.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "nsFtpControlConnection.h"
|
||||
#include "nsIProtocolProxyCallback.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "nsINetworkInterface.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#endif
|
||||
|
||||
// ftp server types
|
||||
#define FTP_GENERIC_TYPE 0
|
||||
#define FTP_UNIX_TYPE 1
|
||||
#define FTP_VMS_TYPE 8
|
||||
#define FTP_NT_TYPE 9
|
||||
#define FTP_OS2_TYPE 11
|
||||
|
||||
// ftp states
|
||||
typedef enum _FTP_STATE {
|
||||
///////////////////////
|
||||
//// Internal states
|
||||
FTP_INIT,
|
||||
FTP_COMMAND_CONNECT,
|
||||
FTP_READ_BUF,
|
||||
FTP_ERROR,
|
||||
FTP_COMPLETE,
|
||||
|
||||
///////////////////////
|
||||
//// Command channel connection setup states
|
||||
FTP_S_USER, FTP_R_USER,
|
||||
FTP_S_PASS, FTP_R_PASS,
|
||||
FTP_S_SYST, FTP_R_SYST,
|
||||
FTP_S_ACCT, FTP_R_ACCT,
|
||||
FTP_S_TYPE, FTP_R_TYPE,
|
||||
FTP_S_CWD, FTP_R_CWD,
|
||||
FTP_S_SIZE, FTP_R_SIZE,
|
||||
FTP_S_MDTM, FTP_R_MDTM,
|
||||
FTP_S_REST, FTP_R_REST,
|
||||
FTP_S_RETR, FTP_R_RETR,
|
||||
FTP_S_STOR, FTP_R_STOR,
|
||||
FTP_S_LIST, FTP_R_LIST,
|
||||
FTP_S_PASV, FTP_R_PASV,
|
||||
FTP_S_PWD, FTP_R_PWD,
|
||||
FTP_S_FEAT, FTP_R_FEAT,
|
||||
FTP_S_OPTS, FTP_R_OPTS
|
||||
} FTP_STATE;
|
||||
|
||||
// higher level ftp actions
|
||||
typedef enum _FTP_ACTION {GET, PUT} FTP_ACTION;
|
||||
|
||||
class nsFtpChannel;
|
||||
class nsICancelable;
|
||||
class nsIProxyInfo;
|
||||
class nsIStreamListener;
|
||||
|
||||
// The nsFtpState object is the content stream for the channel. It implements
|
||||
// nsIInputStreamCallback, so it can read data from the control connection. It
|
||||
// implements nsITransportEventSink so it can mix status events from both the
|
||||
// control connection and the data connection.
|
||||
|
||||
class nsFtpState final : public nsBaseContentStream,
|
||||
public nsIInputStreamCallback,
|
||||
public nsITransportEventSink,
|
||||
public nsIRequestObserver,
|
||||
public nsFtpControlConnectionListener,
|
||||
public nsIProtocolProxyCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
NS_DECL_NSITRANSPORTEVENTSINK
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSIPROTOCOLPROXYCALLBACK
|
||||
|
||||
// Override input stream methods:
|
||||
NS_IMETHOD CloseWithStatus(nsresult status) override;
|
||||
NS_IMETHOD Available(uint64_t *result) override;
|
||||
NS_IMETHOD ReadSegments(nsWriteSegmentFun fun, void *closure,
|
||||
uint32_t count, uint32_t *result) override;
|
||||
|
||||
// nsFtpControlConnectionListener methods:
|
||||
virtual void OnControlDataAvailable(const char *data, uint32_t dataLen) override;
|
||||
virtual void OnControlError(nsresult status) override;
|
||||
|
||||
nsFtpState();
|
||||
nsresult Init(nsFtpChannel *channel);
|
||||
|
||||
protected:
|
||||
// Notification from nsBaseContentStream::AsyncWait
|
||||
virtual void OnCallbackPending() override;
|
||||
|
||||
private:
|
||||
virtual ~nsFtpState();
|
||||
|
||||
///////////////////////////////////
|
||||
// BEGIN: STATE METHODS
|
||||
nsresult S_user(); FTP_STATE R_user();
|
||||
nsresult S_pass(); FTP_STATE R_pass();
|
||||
nsresult S_syst(); FTP_STATE R_syst();
|
||||
nsresult S_acct(); FTP_STATE R_acct();
|
||||
|
||||
nsresult S_type(); FTP_STATE R_type();
|
||||
nsresult S_cwd(); FTP_STATE R_cwd();
|
||||
|
||||
nsresult S_size(); FTP_STATE R_size();
|
||||
nsresult S_mdtm(); FTP_STATE R_mdtm();
|
||||
nsresult S_list(); FTP_STATE R_list();
|
||||
|
||||
nsresult S_rest(); FTP_STATE R_rest();
|
||||
nsresult S_retr(); FTP_STATE R_retr();
|
||||
nsresult S_stor(); FTP_STATE R_stor();
|
||||
nsresult S_pasv(); FTP_STATE R_pasv();
|
||||
nsresult S_pwd(); FTP_STATE R_pwd();
|
||||
nsresult S_feat(); FTP_STATE R_feat();
|
||||
nsresult S_opts(); FTP_STATE R_opts();
|
||||
// END: STATE METHODS
|
||||
///////////////////////////////////
|
||||
|
||||
// internal methods
|
||||
void MoveToNextState(FTP_STATE nextState);
|
||||
nsresult Process();
|
||||
|
||||
void KillControlConnection();
|
||||
nsresult StopProcessing();
|
||||
nsresult EstablishControlConnection();
|
||||
nsresult SendFTPCommand(const nsCSubstring& command);
|
||||
void ConvertFilespecToVMS(nsCString& fileSpec);
|
||||
void ConvertDirspecToVMS(nsCString& fileSpec);
|
||||
void ConvertDirspecFromVMS(nsCString& fileSpec);
|
||||
nsresult BuildStreamConverter(nsIStreamListener** convertStreamListener);
|
||||
nsresult SetContentType();
|
||||
|
||||
/**
|
||||
* This method is called to kick-off the FTP state machine. mState is
|
||||
* reset to FTP_COMMAND_CONNECT, and the FTP state machine progresses from
|
||||
* there. This method is initially called (indirectly) from the channel's
|
||||
* AsyncOpen implementation.
|
||||
*/
|
||||
void Connect();
|
||||
|
||||
///////////////////////////////////
|
||||
// Private members
|
||||
|
||||
// ****** state machine vars
|
||||
FTP_STATE mState; // the current state
|
||||
FTP_STATE mNextState; // the next state
|
||||
bool mKeepRunning; // thread event loop boolean
|
||||
int32_t mResponseCode; // the last command response code
|
||||
nsCString mResponseMsg; // the last command response text
|
||||
|
||||
// ****** channel/transport/stream vars
|
||||
RefPtr<nsFtpControlConnection> mControlConnection; // cacheable control connection (owns mCPipe)
|
||||
bool mReceivedControlData;
|
||||
bool mTryingCachedControl; // retrying the password
|
||||
bool mRETRFailed; // Did we already try a RETR and it failed?
|
||||
uint64_t mFileSize;
|
||||
nsCString mModTime;
|
||||
|
||||
// ****** consumer vars
|
||||
RefPtr<nsFtpChannel> mChannel; // our owning FTP channel we pass through our events
|
||||
nsCOMPtr<nsIProxyInfo> mProxyInfo;
|
||||
|
||||
// ****** connection cache vars
|
||||
int32_t mServerType; // What kind of server are we talking to
|
||||
|
||||
// ****** protocol interpretation related state vars
|
||||
nsString mUsername; // username
|
||||
nsString mPassword; // password
|
||||
FTP_ACTION mAction; // the higher level action (GET/PUT)
|
||||
bool mAnonymous; // try connecting anonymous (default)
|
||||
bool mRetryPass; // retrying the password
|
||||
bool mStorReplyReceived; // FALSE if waiting for STOR
|
||||
// completion status from server
|
||||
nsresult mInternalError; // represents internal state errors
|
||||
bool mReconnectAndLoginAgain;
|
||||
bool mCacheConnection;
|
||||
|
||||
// ****** URI vars
|
||||
int32_t mPort; // the port to connect to
|
||||
nsString mFilename; // url filename (if any)
|
||||
nsCString mPath; // the url's path
|
||||
nsCString mPwd; // login Path
|
||||
|
||||
// ****** other vars
|
||||
nsCOMPtr<nsITransport> mDataTransport;
|
||||
nsCOMPtr<nsIAsyncInputStream> mDataStream;
|
||||
nsCOMPtr<nsIRequest> mUploadRequest;
|
||||
bool mAddressChecked;
|
||||
bool mServerIsIPv6;
|
||||
bool mUseUTF8;
|
||||
|
||||
mozilla::net::NetAddr mServerAddress;
|
||||
|
||||
// ***** control read gvars
|
||||
nsresult mControlStatus;
|
||||
nsCString mControlReadCarryOverBuf;
|
||||
|
||||
nsCString mSuppliedEntityID;
|
||||
|
||||
nsCOMPtr<nsICancelable> mProxyRequest;
|
||||
bool mDeferredCallbackPending;
|
||||
|
||||
// These members are used for network per-app metering (bug 855948)
|
||||
// Currently, they are only available on gonk.
|
||||
uint64_t mCountRecv;
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsMainThreadPtrHandle<nsINetworkInfo> mActiveNetworkInfo;
|
||||
#endif
|
||||
nsresult SaveNetworkStats(bool);
|
||||
void CountRecvBytes(uint64_t recvBytes)
|
||||
{
|
||||
mCountRecv += recvBytes;
|
||||
SaveNetworkStats(false);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //__nsFtpConnectionThread__h_
|
||||
189
netwerk/protocol/ftp/nsFtpControlConnection.cpp
Normal file
189
netwerk/protocol/ftp/nsFtpControlConnection.cpp
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsIOService.h"
|
||||
#include "nsFtpControlConnection.h"
|
||||
#include "nsFtpProtocolHandler.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsISocketTransportService.h"
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsNetCID.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::net;
|
||||
|
||||
extern LazyLogModule gFTPLog;
|
||||
#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
|
||||
#define LOG_INFO(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Info, args)
|
||||
|
||||
//
|
||||
// nsFtpControlConnection implementation ...
|
||||
//
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsFtpControlConnection, nsIInputStreamCallback)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpControlConnection::OnInputStreamReady(nsIAsyncInputStream *stream)
|
||||
{
|
||||
char data[4096];
|
||||
|
||||
// Consume data whether we have a listener or not.
|
||||
uint64_t avail64;
|
||||
uint32_t avail = 0;
|
||||
nsresult rv = stream->Available(&avail64);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
avail = (uint32_t)std::min(avail64, (uint64_t)sizeof(data));
|
||||
|
||||
uint32_t n;
|
||||
rv = stream->Read(data, avail, &n);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
avail = n;
|
||||
}
|
||||
|
||||
// It's important that we null out mListener before calling one of its
|
||||
// methods as it may call WaitData, which would queue up another read.
|
||||
|
||||
RefPtr<nsFtpControlConnectionListener> listener;
|
||||
listener.swap(mListener);
|
||||
|
||||
if (!listener)
|
||||
return NS_OK;
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
listener->OnControlError(rv);
|
||||
} else {
|
||||
listener->OnControlDataAvailable(data, avail);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsFtpControlConnection::nsFtpControlConnection(const nsCSubstring& host,
|
||||
uint32_t port)
|
||||
: mServerType(0), mSessionId(gFtpHandler->GetSessionId())
|
||||
, mUseUTF8(false), mHost(host), mPort(port)
|
||||
{
|
||||
LOG_INFO(("FTP:CC created @%p", this));
|
||||
}
|
||||
|
||||
nsFtpControlConnection::~nsFtpControlConnection()
|
||||
{
|
||||
LOG_INFO(("FTP:CC destroyed @%p", this));
|
||||
}
|
||||
|
||||
bool
|
||||
nsFtpControlConnection::IsAlive()
|
||||
{
|
||||
if (!mSocket)
|
||||
return false;
|
||||
|
||||
bool isAlive = false;
|
||||
mSocket->IsAlive(&isAlive);
|
||||
return isAlive;
|
||||
}
|
||||
nsresult
|
||||
nsFtpControlConnection::Connect(nsIProxyInfo* proxyInfo,
|
||||
nsITransportEventSink* eventSink)
|
||||
{
|
||||
if (mSocket)
|
||||
return NS_OK;
|
||||
|
||||
// build our own
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsISocketTransportService> sts =
|
||||
do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
rv = sts->CreateTransport(nullptr, 0, mHost, mPort, proxyInfo,
|
||||
getter_AddRefs(mSocket)); // the command transport
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
mSocket->SetQoSBits(gFtpHandler->GetControlQoSBits());
|
||||
|
||||
// proxy transport events back to current thread
|
||||
if (eventSink)
|
||||
mSocket->SetEventSink(eventSink, NS_GetCurrentThread());
|
||||
|
||||
// open buffered, blocking output stream to socket. so long as commands
|
||||
// do not exceed 1024 bytes in length, the writing thread (the main thread)
|
||||
// will not block. this should be OK.
|
||||
rv = mSocket->OpenOutputStream(nsITransport::OPEN_BLOCKING, 1024, 1,
|
||||
getter_AddRefs(mSocketOutput));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// open buffered, non-blocking/asynchronous input stream to socket.
|
||||
nsCOMPtr<nsIInputStream> inStream;
|
||||
rv = mSocket->OpenInputStream(0,
|
||||
nsIOService::gDefaultSegmentSize,
|
||||
nsIOService::gDefaultSegmentCount,
|
||||
getter_AddRefs(inStream));
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mSocketInput = do_QueryInterface(inStream);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFtpControlConnection::WaitData(nsFtpControlConnectionListener *listener)
|
||||
{
|
||||
LOG(("FTP:(%p) wait data [listener=%p]\n", this, listener));
|
||||
|
||||
// If listener is null, then simply disconnect the listener. Otherwise,
|
||||
// ensure that we are listening.
|
||||
if (!listener) {
|
||||
mListener = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ENSURE_STATE(mSocketInput);
|
||||
|
||||
mListener = listener;
|
||||
return mSocketInput->AsyncWait(this, 0, 0, NS_GetCurrentThread());
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFtpControlConnection::Disconnect(nsresult status)
|
||||
{
|
||||
if (!mSocket)
|
||||
return NS_OK; // already disconnected
|
||||
|
||||
LOG_INFO(("FTP:(%p) CC disconnecting (%x)", this, status));
|
||||
|
||||
if (NS_FAILED(status)) {
|
||||
// break cyclic reference!
|
||||
mSocket->Close(status);
|
||||
mSocket = nullptr;
|
||||
mSocketInput->AsyncWait(nullptr, 0, 0, nullptr); // clear any observer
|
||||
mSocketInput = nullptr;
|
||||
mSocketOutput = nullptr;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFtpControlConnection::Write(const nsCSubstring& command)
|
||||
{
|
||||
NS_ENSURE_STATE(mSocketOutput);
|
||||
|
||||
uint32_t len = command.Length();
|
||||
uint32_t cnt;
|
||||
nsresult rv = mSocketOutput->Write(command.Data(), len, &cnt);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (len != cnt)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
85
netwerk/protocol/ftp/nsFtpControlConnection.h
Normal file
85
netwerk/protocol/ftp/nsFtpControlConnection.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set et ts=4 sts=4 sw=4 cin: */
|
||||
/* 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 nsFtpControlConnection_h___
|
||||
#define nsFtpControlConnection_h___
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class nsIOutputStream;
|
||||
class nsIProxyInfo;
|
||||
class nsITransportEventSink;
|
||||
|
||||
class nsFtpControlConnectionListener : public nsISupports {
|
||||
public:
|
||||
/**
|
||||
* Called when a chunk of data arrives on the control connection.
|
||||
* @param data
|
||||
* The new data or null if an error occurred.
|
||||
* @param dataLen
|
||||
* The data length in bytes.
|
||||
*/
|
||||
virtual void OnControlDataAvailable(const char *data, uint32_t dataLen) = 0;
|
||||
|
||||
/**
|
||||
* Called when an error occurs on the control connection.
|
||||
* @param status
|
||||
* A failure code providing more info about the error.
|
||||
*/
|
||||
virtual void OnControlError(nsresult status) = 0;
|
||||
};
|
||||
|
||||
class nsFtpControlConnection final : public nsIInputStreamCallback
|
||||
{
|
||||
~nsFtpControlConnection();
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
|
||||
nsFtpControlConnection(const nsCSubstring& host, uint32_t port);
|
||||
|
||||
nsresult Connect(nsIProxyInfo* proxyInfo, nsITransportEventSink* eventSink);
|
||||
nsresult Disconnect(nsresult status);
|
||||
nsresult Write(const nsCSubstring& command);
|
||||
|
||||
bool IsAlive();
|
||||
|
||||
nsITransport *Transport() { return mSocket; }
|
||||
|
||||
/**
|
||||
* Call this function to be notified asynchronously when there is data
|
||||
* available for the socket. The listener passed to this method replaces
|
||||
* any existing listener, and the listener can be null to disconnect the
|
||||
* previous listener.
|
||||
*/
|
||||
nsresult WaitData(nsFtpControlConnectionListener *listener);
|
||||
|
||||
uint32_t mServerType; // what kind of server is it.
|
||||
nsString mPassword;
|
||||
int32_t mSuspendedWrite;
|
||||
nsCString mPwd;
|
||||
uint32_t mSessionId;
|
||||
bool mUseUTF8;
|
||||
|
||||
private:
|
||||
nsCString mHost;
|
||||
uint32_t mPort;
|
||||
|
||||
nsCOMPtr<nsISocketTransport> mSocket;
|
||||
nsCOMPtr<nsIOutputStream> mSocketOutput;
|
||||
nsCOMPtr<nsIAsyncInputStream> mSocketInput;
|
||||
|
||||
RefPtr<nsFtpControlConnectionListener> mListener;
|
||||
};
|
||||
|
||||
#endif
|
||||
426
netwerk/protocol/ftp/nsFtpProtocolHandler.cpp
Normal file
426
netwerk/protocol/ftp/nsFtpProtocolHandler.cpp
Normal file
|
|
@ -0,0 +1,426 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* 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/.
|
||||
*
|
||||
*
|
||||
* This Original Code has been modified by IBM Corporation.
|
||||
* Modifications made by IBM described herein are
|
||||
* Copyright (c) International Business Machines
|
||||
* Corporation, 2000
|
||||
*
|
||||
* Modifications to Mozilla code or documentation
|
||||
* identified per MPL Section 3.3
|
||||
*
|
||||
* Date Modified by Description of modification
|
||||
* 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
|
||||
* use in OS2
|
||||
*/
|
||||
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/net/FTPChannelChild.h"
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::net;
|
||||
|
||||
#include "nsFtpProtocolHandler.h"
|
||||
#include "nsFTPChannel.h"
|
||||
#include "nsIStandardURL.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsEscape.h"
|
||||
#include "nsAlgorithm.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Log module for FTP Protocol logging...
|
||||
//
|
||||
// To enable logging (see prlog.h for full details):
|
||||
//
|
||||
// set MOZ_LOG=nsFtp:5
|
||||
// set MOZ_LOG_FILE=ftp.log
|
||||
//
|
||||
// This enables LogLevel::Debug level information and places all output in
|
||||
// the file ftp.log.
|
||||
//
|
||||
LazyLogModule gFTPLog("nsFtp");
|
||||
#undef LOG
|
||||
#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define IDLE_TIMEOUT_PREF "network.ftp.idleConnectionTimeout"
|
||||
#define IDLE_CONNECTION_LIMIT 8 /* TODO pref me */
|
||||
|
||||
#define QOS_DATA_PREF "network.ftp.data.qos"
|
||||
#define QOS_CONTROL_PREF "network.ftp.control.qos"
|
||||
|
||||
nsFtpProtocolHandler *gFtpHandler = nullptr;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsFtpProtocolHandler::nsFtpProtocolHandler()
|
||||
: mIdleTimeout(-1)
|
||||
, mSessionId(0)
|
||||
, mControlQoSBits(0x00)
|
||||
, mDataQoSBits(0x00)
|
||||
{
|
||||
LOG(("FTP:creating handler @%x\n", this));
|
||||
|
||||
gFtpHandler = this;
|
||||
}
|
||||
|
||||
nsFtpProtocolHandler::~nsFtpProtocolHandler()
|
||||
{
|
||||
LOG(("FTP:destroying handler @%x\n", this));
|
||||
|
||||
NS_ASSERTION(mRootConnectionList.Length() == 0, "why wasn't Observe called?");
|
||||
|
||||
gFtpHandler = nullptr;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsFtpProtocolHandler,
|
||||
nsIProtocolHandler,
|
||||
nsIProxiedProtocolHandler,
|
||||
nsIObserver,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
nsresult
|
||||
nsFtpProtocolHandler::Init()
|
||||
{
|
||||
if (IsNeckoChild())
|
||||
NeckoChild::InitNeckoChild();
|
||||
|
||||
if (mIdleTimeout == -1) {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = branch->GetIntPref(IDLE_TIMEOUT_PREF, &mIdleTimeout);
|
||||
if (NS_FAILED(rv))
|
||||
mIdleTimeout = 5*60; // 5 minute default
|
||||
|
||||
rv = branch->AddObserver(IDLE_TIMEOUT_PREF, this, true);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
int32_t val;
|
||||
rv = branch->GetIntPref(QOS_DATA_PREF, &val);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mDataQoSBits = (uint8_t) clamped(val, 0, 0xff);
|
||||
|
||||
rv = branch->AddObserver(QOS_DATA_PREF, this, true);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = branch->GetIntPref(QOS_CONTROL_PREF, &val);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mControlQoSBits = (uint8_t) clamped(val, 0, 0xff);
|
||||
|
||||
rv = branch->AddObserver(QOS_CONTROL_PREF, this, true);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (observerService) {
|
||||
observerService->AddObserver(this,
|
||||
"network:offline-about-to-go-offline",
|
||||
true);
|
||||
|
||||
observerService->AddObserver(this,
|
||||
"net:clear-active-logins",
|
||||
true);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIProtocolHandler methods:
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpProtocolHandler::GetScheme(nsACString &result)
|
||||
{
|
||||
result.AssignLiteral("ftp");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpProtocolHandler::GetDefaultPort(int32_t *result)
|
||||
{
|
||||
*result = 21;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpProtocolHandler::GetProtocolFlags(uint32_t *result)
|
||||
{
|
||||
*result = URI_STD | ALLOWS_PROXY | ALLOWS_PROXY_HTTP |
|
||||
URI_LOADABLE_BY_ANYONE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpProtocolHandler::NewURI(const nsACString &aSpec,
|
||||
const char *aCharset,
|
||||
nsIURI *aBaseURI,
|
||||
nsIURI **result)
|
||||
{
|
||||
nsAutoCString spec(aSpec);
|
||||
spec.Trim(" \t\n\r"); // Match NS_IsAsciiWhitespace instead of HTML5
|
||||
|
||||
char *fwdPtr = spec.BeginWriting();
|
||||
|
||||
// now unescape it... %xx reduced inline to resulting character
|
||||
|
||||
int32_t len = NS_UnescapeURL(fwdPtr);
|
||||
|
||||
// NS_UnescapeURL() modified spec's buffer, truncate to ensure
|
||||
// spec knows its new length.
|
||||
spec.Truncate(len);
|
||||
|
||||
// return an error if we find a NUL, CR, or LF in the path
|
||||
if (spec.FindCharInSet(CRLF) >= 0 || spec.FindChar('\0') >= 0)
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIStandardURL> url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY, 21, aSpec, aCharset, aBaseURI);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return CallQueryInterface(url, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpProtocolHandler::NewChannel2(nsIURI* url,
|
||||
nsILoadInfo* aLoadInfo,
|
||||
nsIChannel** result)
|
||||
{
|
||||
return NewProxiedChannel2(url, nullptr, 0, nullptr, aLoadInfo, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpProtocolHandler::NewChannel(nsIURI* url, nsIChannel* *result)
|
||||
{
|
||||
return NewChannel2(url, nullptr, result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpProtocolHandler::NewProxiedChannel2(nsIURI* uri, nsIProxyInfo* proxyInfo,
|
||||
uint32_t proxyResolveFlags,
|
||||
nsIURI *proxyURI,
|
||||
nsILoadInfo* aLoadInfo,
|
||||
nsIChannel* *result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(uri);
|
||||
RefPtr<nsBaseChannel> channel;
|
||||
if (IsNeckoChild())
|
||||
channel = new FTPChannelChild(uri);
|
||||
else
|
||||
channel = new nsFtpChannel(uri, proxyInfo);
|
||||
|
||||
nsresult rv = channel->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// set the loadInfo on the new channel
|
||||
rv = channel->SetLoadInfo(aLoadInfo);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
channel.forget(result);
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpProtocolHandler::NewProxiedChannel(nsIURI* uri, nsIProxyInfo* proxyInfo,
|
||||
uint32_t proxyResolveFlags,
|
||||
nsIURI *proxyURI,
|
||||
nsIChannel* *result)
|
||||
{
|
||||
return NewProxiedChannel2(uri, proxyInfo, proxyResolveFlags,
|
||||
proxyURI, nullptr /*loadinfo*/,
|
||||
result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
|
||||
{
|
||||
*_retval = (port == 21 || port == 22);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// connection cache methods
|
||||
|
||||
void
|
||||
nsFtpProtocolHandler::Timeout(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
LOG(("FTP:timeout reached for %p\n", aClosure));
|
||||
|
||||
bool found = gFtpHandler->mRootConnectionList.RemoveElement(aClosure);
|
||||
if (!found) {
|
||||
NS_ERROR("timerStruct not found");
|
||||
return;
|
||||
}
|
||||
|
||||
timerStruct* s = (timerStruct*)aClosure;
|
||||
delete s;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFtpProtocolHandler::RemoveConnection(nsIURI *aKey, nsFtpControlConnection* *_retval)
|
||||
{
|
||||
NS_ASSERTION(_retval, "null pointer");
|
||||
NS_ASSERTION(aKey, "null pointer");
|
||||
|
||||
*_retval = nullptr;
|
||||
|
||||
nsAutoCString spec;
|
||||
aKey->GetPrePath(spec);
|
||||
|
||||
LOG(("FTP:removing connection for %s\n", spec.get()));
|
||||
|
||||
timerStruct* ts = nullptr;
|
||||
uint32_t i;
|
||||
bool found = false;
|
||||
|
||||
for (i=0;i<mRootConnectionList.Length();++i) {
|
||||
ts = mRootConnectionList[i];
|
||||
if (strcmp(spec.get(), ts->key) == 0) {
|
||||
found = true;
|
||||
mRootConnectionList.RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// swap connection ownership
|
||||
ts->conn.forget(_retval);
|
||||
delete ts;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFtpProtocolHandler::InsertConnection(nsIURI *aKey, nsFtpControlConnection *aConn)
|
||||
{
|
||||
NS_ASSERTION(aConn, "null pointer");
|
||||
NS_ASSERTION(aKey, "null pointer");
|
||||
|
||||
if (aConn->mSessionId != mSessionId)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsAutoCString spec;
|
||||
aKey->GetPrePath(spec);
|
||||
|
||||
LOG(("FTP:inserting connection for %s\n", spec.get()));
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
timerStruct* ts = new timerStruct();
|
||||
if (!ts)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
rv = timer->InitWithFuncCallback(nsFtpProtocolHandler::Timeout,
|
||||
ts,
|
||||
mIdleTimeout*1000,
|
||||
nsITimer::TYPE_REPEATING_SLACK);
|
||||
if (NS_FAILED(rv)) {
|
||||
delete ts;
|
||||
return rv;
|
||||
}
|
||||
|
||||
ts->key = ToNewCString(spec);
|
||||
if (!ts->key) {
|
||||
delete ts;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// ts->conn is a RefPtr
|
||||
ts->conn = aConn;
|
||||
ts->timer = timer;
|
||||
|
||||
//
|
||||
// limit number of idle connections. if limit is reached, then prune
|
||||
// eldest connection with matching key. if none matching, then prune
|
||||
// eldest connection.
|
||||
//
|
||||
if (mRootConnectionList.Length() == IDLE_CONNECTION_LIMIT) {
|
||||
uint32_t i;
|
||||
for (i=0;i<mRootConnectionList.Length();++i) {
|
||||
timerStruct *candidate = mRootConnectionList[i];
|
||||
if (strcmp(candidate->key, ts->key) == 0) {
|
||||
mRootConnectionList.RemoveElementAt(i);
|
||||
delete candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mRootConnectionList.Length() == IDLE_CONNECTION_LIMIT) {
|
||||
timerStruct *eldest = mRootConnectionList[0];
|
||||
mRootConnectionList.RemoveElementAt(0);
|
||||
delete eldest;
|
||||
}
|
||||
}
|
||||
|
||||
mRootConnectionList.AppendElement(ts);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIObserver
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpProtocolHandler::Observe(nsISupports *aSubject,
|
||||
const char *aTopic,
|
||||
const char16_t *aData)
|
||||
{
|
||||
LOG(("FTP:observing [%s]\n", aTopic));
|
||||
|
||||
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
||||
nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(aSubject);
|
||||
if (!branch) {
|
||||
NS_ERROR("no prefbranch");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
int32_t val;
|
||||
nsresult rv = branch->GetIntPref(IDLE_TIMEOUT_PREF, &val);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mIdleTimeout = val;
|
||||
|
||||
rv = branch->GetIntPref(QOS_DATA_PREF, &val);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mDataQoSBits = (uint8_t) clamped(val, 0, 0xff);
|
||||
|
||||
rv = branch->GetIntPref(QOS_CONTROL_PREF, &val);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mControlQoSBits = (uint8_t) clamped(val, 0, 0xff);
|
||||
} else if (!strcmp(aTopic, "network:offline-about-to-go-offline")) {
|
||||
ClearAllConnections();
|
||||
} else if (!strcmp(aTopic, "net:clear-active-logins")) {
|
||||
ClearAllConnections();
|
||||
mSessionId++;
|
||||
} else {
|
||||
NS_NOTREACHED("unexpected topic");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsFtpProtocolHandler::ClearAllConnections()
|
||||
{
|
||||
uint32_t i;
|
||||
for (i=0;i<mRootConnectionList.Length();++i)
|
||||
delete mRootConnectionList[i];
|
||||
mRootConnectionList.Clear();
|
||||
}
|
||||
87
netwerk/protocol/ftp/nsFtpProtocolHandler.h
Normal file
87
netwerk/protocol/ftp/nsFtpProtocolHandler.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 nsFtpProtocolHandler_h__
|
||||
#define nsFtpProtocolHandler_h__
|
||||
|
||||
#include "nsFtpControlConnection.h"
|
||||
#include "nsIProxiedProtocolHandler.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsFtpProtocolHandler final : public nsIProxiedProtocolHandler
|
||||
, public nsIObserver
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIPROTOCOLHANDLER
|
||||
NS_DECL_NSIPROXIEDPROTOCOLHANDLER
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
nsFtpProtocolHandler();
|
||||
|
||||
nsresult Init();
|
||||
|
||||
// FTP Connection list access
|
||||
nsresult InsertConnection(nsIURI *aKey, nsFtpControlConnection *aConn);
|
||||
nsresult RemoveConnection(nsIURI *aKey, nsFtpControlConnection **aConn);
|
||||
uint32_t GetSessionId() { return mSessionId; }
|
||||
|
||||
uint8_t GetDataQoSBits() { return mDataQoSBits; }
|
||||
uint8_t GetControlQoSBits() { return mControlQoSBits; }
|
||||
|
||||
private:
|
||||
virtual ~nsFtpProtocolHandler();
|
||||
|
||||
// Stuff for the timer callback function
|
||||
struct timerStruct {
|
||||
nsCOMPtr<nsITimer> timer;
|
||||
RefPtr<nsFtpControlConnection> conn;
|
||||
char *key;
|
||||
|
||||
timerStruct() : key(nullptr) {}
|
||||
|
||||
~timerStruct() {
|
||||
if (timer)
|
||||
timer->Cancel();
|
||||
if (key)
|
||||
free(key);
|
||||
if (conn) {
|
||||
conn->Disconnect(NS_ERROR_ABORT);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void Timeout(nsITimer *aTimer, void *aClosure);
|
||||
void ClearAllConnections();
|
||||
|
||||
nsTArray<timerStruct*> mRootConnectionList;
|
||||
|
||||
int32_t mIdleTimeout;
|
||||
|
||||
// When "clear active logins" is performed, all idle connection are dropped
|
||||
// and mSessionId is incremented. When nsFtpState wants to insert idle
|
||||
// connection we refuse to cache if its mSessionId is different (i.e.
|
||||
// control connection had been created before last "clear active logins" was
|
||||
// performed.
|
||||
uint32_t mSessionId;
|
||||
|
||||
uint8_t mControlQoSBits;
|
||||
uint8_t mDataQoSBits;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
extern nsFtpProtocolHandler *gFtpHandler;
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
extern mozilla::LazyLogModule gFTPLog;
|
||||
|
||||
#endif // !nsFtpProtocolHandler_h__
|
||||
29
netwerk/protocol/ftp/nsIFTPChannel.idl
Normal file
29
netwerk/protocol/ftp/nsIFTPChannel.idl
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* This interface may be used to determine if a channel is a FTP channel.
|
||||
*/
|
||||
[scriptable, uuid(07f0d5cd-1fd5-4aa3-b6fc-665bdc5dbf9f)]
|
||||
interface nsIFTPChannel : nsISupports
|
||||
{
|
||||
attribute PRTime lastModifiedTime;
|
||||
};
|
||||
|
||||
/**
|
||||
* This interface may be defined as a notification callback on the FTP
|
||||
* channel. It allows a consumer to receive a log of the FTP control
|
||||
* connection conversation.
|
||||
*/
|
||||
[scriptable, uuid(455d4234-0330-43d2-bbfb-99afbecbfeb0)]
|
||||
interface nsIFTPEventSink : nsISupports
|
||||
{
|
||||
/**
|
||||
* XXX document this method! (see bug 328915)
|
||||
*/
|
||||
void OnFTPControlLog(in boolean server, in string msg);
|
||||
};
|
||||
15
netwerk/protocol/ftp/nsIFTPChannelParentInternal.idl
Normal file
15
netwerk/protocol/ftp/nsIFTPChannelParentInternal.idl
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* This is an internal interface for FTP parent channel.
|
||||
*/
|
||||
[builtinclass, uuid(87b58410-83cb-42a7-b57b-27c07ef828d7)]
|
||||
interface nsIFTPChannelParentInternal : nsISupports
|
||||
{
|
||||
void setErrorMsg(in string msg, in boolean useUTF8);
|
||||
};
|
||||
5
netwerk/protocol/ftp/test/frametest/contents.html
Normal file
5
netwerk/protocol/ftp/test/frametest/contents.html
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
<h2>Click a link to the left</h2>
|
||||
</body>
|
||||
</html>
|
||||
13
netwerk/protocol/ftp/test/frametest/index.html
Normal file
13
netwerk/protocol/ftp/test/frametest/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>FTP Frameset Test</title>
|
||||
|
||||
</head>
|
||||
|
||||
<frameset cols="30%,70%" name="ftp_main_frame">
|
||||
<frame src="menu.html" name="ftp_menu" scrolling="yes" marginwidth="0" marginheight="0" noresize>
|
||||
<frame src="contents.html" name="ftp_content" scrolling="YES" marginwidth="0" marginheight="0" noresize>
|
||||
</frameset>
|
||||
|
||||
</html>
|
||||
|
||||
373
netwerk/protocol/ftp/test/frametest/menu.html
Normal file
373
netwerk/protocol/ftp/test/frametest/menu.html
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
<html>
|
||||
|
||||
<script language="javascript">
|
||||
|
||||
<!--
|
||||
|
||||
function add_location(url)
|
||||
{
|
||||
// faster to do this in one assignment, but who really cares.
|
||||
|
||||
var string = '<LI> <a href=\"javascript: ftp_open(\'';
|
||||
string += url;
|
||||
string += '\')\"';
|
||||
string += 'onmouseover=\"window.status=\'ftp://';
|
||||
string += url;
|
||||
string += '\'; return true; \">';
|
||||
string += url;
|
||||
string += '</a>';
|
||||
document.writeln(string);
|
||||
}
|
||||
|
||||
function ftp_open(url)
|
||||
{
|
||||
url = 'ftp://' + url;
|
||||
parent.ftp_content.location=url;
|
||||
}
|
||||
|
||||
// I like this format.
|
||||
document.writeln('<pre>');
|
||||
|
||||
document.writeln('<br><OL><b>Company sites</b>');
|
||||
add_location('ftp.mozilla.org');
|
||||
add_location('ftp.sun.com');
|
||||
add_location('ftp.iplanet.com');
|
||||
add_location('ftp.netscape.com');
|
||||
add_location('ftp.apple.com');
|
||||
add_location('ftp.microsoft.com');
|
||||
add_location('ftp.netmanage.com');
|
||||
|
||||
document.writeln('</OL><br><OL><b>Misc sites</b>');
|
||||
add_location('cal044202.student.utwente.nl');
|
||||
add_location('download.intel.com');
|
||||
add_location('fddisunsite.oit.unc.edu');
|
||||
add_location('ftp.abas.de');
|
||||
add_location('ftp.acm.org');
|
||||
add_location('ftp.acomp.hu');
|
||||
add_location('ftp.acri.fr');
|
||||
add_location('ftp.alaska.edu');
|
||||
add_location('ftp.altera.com');
|
||||
add_location('ftp.amsat.org');
|
||||
add_location('ftp.amtp.cam.ac.uk');
|
||||
add_location('ftp.ar.freebsd.org');
|
||||
add_location('ftp.ari.net');
|
||||
add_location('ftp.arl.mil');
|
||||
add_location('ftp.astro.ulg.ac.be');
|
||||
add_location('ftp.avery-zweckform.com');
|
||||
add_location('ftp.awi-bremerhaven.de');
|
||||
add_location('ftp.axime-is.fr');
|
||||
add_location('ftp.ba.cnr.it');
|
||||
add_location('ftp.bath.ac.uk');
|
||||
add_location('ftp.bic.mni.mcgill.ca');
|
||||
add_location('ftp.biomed.ruhr-uni-bochum.de');
|
||||
add_location('ftp.boerde.de');
|
||||
add_location('ftp.bond.edu.au');
|
||||
add_location('ftp.boulder.ibm.com');
|
||||
add_location('ftp.brics.dk');
|
||||
add_location('ftp.bris.ac.uk');
|
||||
add_location('ftp.cablelabs.com');
|
||||
add_location('ftp.cac.psu.edu');
|
||||
add_location('ftp.cadpoint.se');
|
||||
add_location('ftp.cas.cz');
|
||||
add_location('ftp.cciw.ca');
|
||||
add_location('ftp.ccs.queensu.ca');
|
||||
add_location('ftp.ccsi.com');
|
||||
add_location('ftp.cdrom.com');
|
||||
add_location('ftp.cea.fr');
|
||||
add_location('ftp.celestial.com');
|
||||
add_location('ftp.cert.fr');
|
||||
add_location('ftp.cgd.ucar.edu');
|
||||
add_location('ftp.chiba-u.ac.jp');
|
||||
add_location('ftp.cis.ksu.edu');
|
||||
add_location('ftp.citi2.fr');
|
||||
add_location('ftp.cityline.net');
|
||||
add_location('ftp.cnam.fr');
|
||||
add_location('ftp.cohesive.com');
|
||||
add_location('ftp.contrib.net');
|
||||
add_location('ftp.create.ucsb.edu');
|
||||
add_location('ftp.cronyx.ru');
|
||||
add_location('ftp.cs.arizona.edu');
|
||||
add_location('ftp.cs.colorado.edu');
|
||||
add_location('ftp.cs.concordia.ca');
|
||||
add_location('ftp.cs.helsinki.fi');
|
||||
add_location('ftp.cs.jhu.edu');
|
||||
add_location('ftp.cs.monash.edu.au');
|
||||
add_location('ftp.cs.ohiou.edu');
|
||||
add_location('ftp.cs.rug.nl');
|
||||
add_location('ftp.cs.toronto.edu');
|
||||
add_location('ftp.cs.umanitoba.ca');
|
||||
add_location('ftp.cs.uni-dortmund.de');
|
||||
add_location('ftp.cs.vu.nl');
|
||||
add_location('ftp.cse.cuhk.edu.hk');
|
||||
add_location('ftp.cse.unsw.edu.au');
|
||||
add_location('ftp.csse.monash.edu.au');
|
||||
add_location('ftp.csus.edu');
|
||||
add_location('ftp.cullasaja.com');
|
||||
add_location('ftp.daimi.au.dk');
|
||||
add_location('ftp.dcs.qmw.ac.uk');
|
||||
add_location('ftp.delorie.com');
|
||||
add_location('ftp.dementia.org');
|
||||
add_location('ftp.dfki.uni-kl.de');
|
||||
add_location('ftp.dgs.monash.edu.au');
|
||||
add_location('ftp.dis.strath.ac.uk');
|
||||
add_location('ftp.dosis.uni-dortmund.de');
|
||||
add_location('ftp.duke.edu');
|
||||
add_location('ftp.duplexx.com');
|
||||
add_location('ftp.ece.ucdavis.edu');
|
||||
add_location('ftp.ee.lbl.gov');
|
||||
add_location('ftp.ee.rochester.edu');
|
||||
add_location('ftp.ee.uts.edu.au');
|
||||
add_location('ftp.efrei.fr');
|
||||
add_location('ftp.elet.polimi.it');
|
||||
add_location('ftp.elite.net');
|
||||
add_location('ftp.embl-hamburg.de');
|
||||
add_location('ftp.eng.buffalo.edu');
|
||||
add_location('ftp.engr.uark.edu');
|
||||
add_location('ftp.eni.co.jp');
|
||||
add_location('ftp.enst-bretagne.fr');
|
||||
add_location('ftp.epix.net');
|
||||
add_location('ftp.eskimo.com');
|
||||
add_location('ftp.essential.org');
|
||||
add_location('ftp.eunet.fi');
|
||||
add_location('ftp.eurexpo.com');
|
||||
add_location('ftp.ex.ac.uk');
|
||||
add_location('ftp.faximum.com');
|
||||
add_location('ftp.fernuni-hagen.de');
|
||||
add_location('ftp.fh-dortmund.de');
|
||||
add_location('ftp.fit.qut.edu.au');
|
||||
add_location('ftp.forum.swarthmore.edu');
|
||||
add_location('ftp.fsu.edu');
|
||||
add_location('ftp.ftp.epson.com');
|
||||
add_location('ftp.fu-berlin.de');
|
||||
add_location('ftp.fujixerox.co.jp');
|
||||
add_location('ftp.game.org');
|
||||
add_location('ftp.ge.ucl.ac.uk');
|
||||
add_location('ftp.genetics.wisc.edu');
|
||||
add_location('ftp.geo.uu.nl');
|
||||
add_location('ftp.geom.umn.edu');
|
||||
add_location('ftp.gfdl.gov');
|
||||
add_location('ftp.gigo.com');
|
||||
add_location('ftp.giss.nasa.gov');
|
||||
add_location('ftp.globalnet.co.uk');
|
||||
add_location('ftp.gnu.org');
|
||||
add_location('ftp.gnu.vbs.at');
|
||||
add_location('ftp.gps.caltech.edu');
|
||||
add_location('ftp.grau-wzs.de');
|
||||
add_location('ftp.gsoc.dlr.de');
|
||||
add_location('ftp.gutenberg.org');
|
||||
add_location('ftp.hawaii.edu');
|
||||
add_location('ftp.hep.net');
|
||||
add_location('ftp.hgc.edu');
|
||||
add_location('ftp.hgmp.mrc.ac.uk');
|
||||
add_location('ftp.hugin.dk');
|
||||
add_location('ftp.ic.tsu.ru');
|
||||
add_location('ftp.icce.rug.nl');
|
||||
add_location('ftp.icon-stl.net');
|
||||
add_location('ftp.icor.fr');
|
||||
add_location('ftp.ics.uci.edu');
|
||||
add_location('ftp.idsia.ch');
|
||||
add_location('ftp.ifm.liu.se');
|
||||
add_location('ftp.ifm.uni-kiel.de');
|
||||
add_location('ftp.iglou.com');
|
||||
add_location('ftp.ign.fr');
|
||||
add_location('ftp.imag.fr');
|
||||
add_location('ftp.inel.gov');
|
||||
add_location('ftp.inf.ethz.ch');
|
||||
add_location('ftp.inf.puc-rio.br');
|
||||
add_location('ftp.infoflex.se');
|
||||
add_location('ftp.informatik.rwth-aachen.de');
|
||||
add_location('ftp.informatik.uni-bremen.de');
|
||||
add_location('ftp.informatik.uni-hannover.de');
|
||||
add_location('ftp.infoscandic.se');
|
||||
add_location('ftp.intel.com');
|
||||
add_location('ftp.intergraph.com');
|
||||
add_location('ftp.ionet.net');
|
||||
add_location('ftp.ipc.chiba-u.ac.jp');
|
||||
add_location('ftp.ips.cs.tu-bs.de');
|
||||
add_location('ftp.iram.rwth-aachen.de');
|
||||
add_location('ftp.is.co.za');
|
||||
add_location('ftp.isoc.org');
|
||||
add_location('ftp.iteso.mx');
|
||||
add_location('ftp.ivd.uni-stuttgart.de');
|
||||
add_location('ftp.iway.fr');
|
||||
add_location('ftp.jcu.edu.au');
|
||||
add_location('ftp.jhuapl.edu');
|
||||
add_location('ftp.jpix.ad.jp');
|
||||
add_location('ftp.karlin.mff.cuni.cz');
|
||||
add_location('ftp.kfu.com');
|
||||
add_location('ftp.kfunigraz.ac.at');
|
||||
add_location('ftp.khm.de');
|
||||
add_location('ftp.ki.se');
|
||||
add_location('ftp.komkon.org');
|
||||
add_location('ftp.laas.fr');
|
||||
add_location('ftp.lanl.gov');
|
||||
add_location('ftp.lantronix.com');
|
||||
add_location('ftp.lava.net');
|
||||
add_location('ftp.lcs.mit.edu');
|
||||
add_location('ftp.legend.co.uk');
|
||||
add_location('ftp.leidenuniv.nl');
|
||||
add_location('ftp.let.rug.nl');
|
||||
add_location('ftp.linux.co.uk');
|
||||
add_location('ftp.linux.unife.it');
|
||||
add_location('ftp.liv.ac.uk');
|
||||
add_location('ftp.livingston.com');
|
||||
add_location('ftp.lnt.e-technik.tu-muenchen.de');
|
||||
add_location('ftp.lsu.edu');
|
||||
add_location('ftp.lth.se');
|
||||
add_location('ftp.lysator.liu.se');
|
||||
add_location('ftp.mailbase.ac.uk');
|
||||
add_location('ftp.mainstream.net');
|
||||
add_location('ftp.maricopa.edu');
|
||||
add_location('ftp.math.fu-berlin.de');
|
||||
add_location('ftp.math.hr');
|
||||
add_location('ftp.math.utah.edu');
|
||||
add_location('ftp.mathematik.uni-marburg.de');
|
||||
add_location('ftp.maths.tcd.ie');
|
||||
add_location('ftp.maths.usyd.edu.au');
|
||||
add_location('ftp.mathworks.com');
|
||||
add_location('ftp.mbb.ki.se');
|
||||
add_location('ftp.mbt.ru');
|
||||
add_location('ftp.mcs.net');
|
||||
add_location('ftp.mcs.vuw.ac.nz');
|
||||
add_location('ftp.media.mit.edu');
|
||||
add_location('ftp.meme.com');
|
||||
add_location('ftp.merl.com');
|
||||
add_location('ftp.microport.com');
|
||||
add_location('ftp.mms.de');
|
||||
add_location('ftp.mpce.mq.edu.au');
|
||||
add_location('ftp.mpgn.com');
|
||||
add_location('ftp.mpipf-muenchen.mpg.de');
|
||||
add_location('ftp.mscf.uky.edu');
|
||||
add_location('ftp.natinst.com');
|
||||
add_location('ftp.ncsa.uiuc.edu');
|
||||
add_location('ftp.net-tel.co.uk');
|
||||
add_location('ftp.net.cmu.edu');
|
||||
add_location('ftp.netsw.org');
|
||||
add_location('ftp.new-york.net');
|
||||
add_location('ftp.nis.net');
|
||||
add_location('ftp.nlm.nih.gov');
|
||||
add_location('ftp.nmt.edu');
|
||||
add_location('ftp.noao.edu');
|
||||
add_location('ftp.ntnu.no');
|
||||
add_location('ftp.nwu.edu');
|
||||
add_location('ftp.nysaes.cornell.edu');
|
||||
add_location('ftp.observ.u-bordeaux.fr');
|
||||
add_location('ftp.oit.unc.edu');
|
||||
add_location('ftp.oldenbourg.de');
|
||||
add_location('ftp.omg.unb.ca');
|
||||
add_location('ftp.onecall.net');
|
||||
add_location('ftp.ornl.gov');
|
||||
add_location('ftp.ozone.fmi.fi');
|
||||
add_location('ftp.pacific.net.hk');
|
||||
add_location('ftp.panix.com');
|
||||
add_location('ftp.pcnet.com');
|
||||
add_location('ftp.phred.org');
|
||||
add_location('ftp.pnl.gov');
|
||||
add_location('ftp.prairienet.org');
|
||||
add_location('ftp.proxad.net');
|
||||
add_location('ftp.proximity.com.au');
|
||||
add_location('ftp.psg.com');
|
||||
add_location('ftp.psy.uq.edu.au');
|
||||
add_location('ftp.psychologie.uni-freiburg.de');
|
||||
add_location('ftp.pwr.wroc.pl');
|
||||
add_location('ftp.python.org');
|
||||
add_location('ftp.quantum.de');
|
||||
add_location('ftp.ra.phy.cam.ac.uk');
|
||||
add_location('ftp.rasip.fer.hr');
|
||||
add_location('ftp.rbgkew.org.uk');
|
||||
add_location('ftp.rcsb.org');
|
||||
add_location('ftp.realtime.net');
|
||||
add_location('ftp.red-bean.com');
|
||||
add_location('ftp.redac.co.uk');
|
||||
add_location('ftp.redac.fr');
|
||||
add_location('ftp.rediris.es');
|
||||
add_location('ftp.rgn.it');
|
||||
add_location('ftp.rice.edu');
|
||||
add_location('ftp.rkk.hu');
|
||||
add_location('ftp.robelle.com');
|
||||
add_location('ftp.rose.hp.com');
|
||||
add_location('ftp.rt66.com');
|
||||
add_location('ftp.ruhr-uni-bochum.de');
|
||||
add_location('ftp.rz.uni-frankfurt.de');
|
||||
add_location('ftp.sat.dundee.ac.uk');
|
||||
add_location('ftp.saugus.net');
|
||||
add_location('ftp.schrodinger.com');
|
||||
add_location('ftp.science-computing.de');
|
||||
add_location('ftp.science.unitn.it');
|
||||
add_location('ftp.sco.com');
|
||||
add_location('ftp.scs.leeds.ac.uk');
|
||||
add_location('ftp.scsr.nevada.edu');
|
||||
add_location('ftp.sd.monash.edu.au');
|
||||
add_location('ftp.sdv.fr');
|
||||
add_location('ftp.selapo.vwl.uni-muenchen.de');
|
||||
add_location('ftp.serv.net');
|
||||
add_location('ftp.sgi.leeds.ac.uk');
|
||||
add_location('ftp.shore.net');
|
||||
add_location('ftp.socsci.auc.dk');
|
||||
add_location('ftp.space.net');
|
||||
add_location('ftp.spec.org');
|
||||
add_location('ftp.stallion.com');
|
||||
add_location('ftp.starnet.de');
|
||||
add_location('ftp.stat.math.ethz.ch');
|
||||
add_location('ftp.stat.umn.edu');
|
||||
add_location('ftp.std.com');
|
||||
add_location('ftp.structchem.uni-essen.de');
|
||||
add_location('ftp.sunsite.org.uk');
|
||||
add_location('ftp.syd.dms.csiro.au');
|
||||
add_location('ftp.tapr.org');
|
||||
add_location('ftp.teco.uni-karlsruhe.de');
|
||||
add_location('ftp.tenon.com');
|
||||
add_location('ftp.tierzucht.uni-kiel.de');
|
||||
add_location('ftp.tnt.uni-hannover.de');
|
||||
add_location('ftp.tu-clausthal.de');
|
||||
add_location('ftp.uci.edu');
|
||||
add_location('ftp.ucsd.edu');
|
||||
add_location('ftp.udel.edu');
|
||||
add_location('ftp.uec.ac.jp');
|
||||
add_location('ftp.uibk.ac.at');
|
||||
add_location('ftp.uit.co.uk');
|
||||
add_location('ftp.uji.es');
|
||||
add_location('ftp.uke.uni-hamburg.de');
|
||||
add_location('ftp.ulcc.ac.uk');
|
||||
add_location('ftp.um.es');
|
||||
add_location('ftp.umi.cs.tu-bs.de');
|
||||
add_location('ftp.uni-augsburg.de');
|
||||
add_location('ftp.uni-dortmund.de');
|
||||
add_location('ftp.uni-hannover.de');
|
||||
add_location('ftp.uni-magdeburg.de');
|
||||
add_location('ftp.unidata.ucar.edu');
|
||||
add_location('ftp.unige.ch');
|
||||
add_location('ftp.univ-aix.fr');
|
||||
add_location('ftp.upc.es');
|
||||
add_location('ftp.uradio.ku.dk');
|
||||
add_location('ftp.uralexpress.ru');
|
||||
add_location('ftp.urc.ac.ru');
|
||||
add_location('ftp.ut.ee');
|
||||
add_location('ftp.uunet.ca');
|
||||
add_location('ftp.uwo.ca');
|
||||
add_location('ftp.vaxxine.com');
|
||||
add_location('ftp.visi.com');
|
||||
add_location('ftp.vub.ac.be');
|
||||
add_location('ftp.wfu.edu');
|
||||
add_location('ftp.win.tue.nl');
|
||||
add_location('ftp.wolfe.net');
|
||||
add_location('sunsite.cnlab-switch.ch');
|
||||
add_location('sunsite.sut.ac.jp');
|
||||
add_location('ftp.cuhk.edu.hk');
|
||||
add_location('ftp.cetis.hvu.nl');
|
||||
add_location('ftp.clinet.fi');
|
||||
add_location('ftp.gamma.ru');
|
||||
add_location('ftp.itv.se');
|
||||
add_location('ftp.cs.rpi.edu');
|
||||
add_location('ftp.carrier.kiev.ua');
|
||||
add_location('ftp.rosnet.ru');
|
||||
add_location('ftp.nsk.su');
|
||||
add_location('ftp.southcom.com.au');
|
||||
|
||||
// -->
|
||||
|
||||
</script>
|
||||
<body>
|
||||
<br><br><br>
|
||||
</body>
|
||||
</html>
|
||||
127
netwerk/protocol/http/ASpdySession.cpp
Normal file
127
netwerk/protocol/http/ASpdySession.cpp
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/. */
|
||||
|
||||
// HttpLog.h should generally be included first
|
||||
#include "HttpLog.h"
|
||||
|
||||
/*
|
||||
Currently supported is h2
|
||||
*/
|
||||
|
||||
#include "nsHttp.h"
|
||||
#include "nsHttpHandler.h"
|
||||
|
||||
#include "ASpdySession.h"
|
||||
#include "PSpdyPush.h"
|
||||
#include "Http2Push.h"
|
||||
#include "Http2Session.h"
|
||||
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
ASpdySession::ASpdySession()
|
||||
{
|
||||
}
|
||||
|
||||
ASpdySession::~ASpdySession() = default;
|
||||
|
||||
ASpdySession *
|
||||
ASpdySession::NewSpdySession(uint32_t version,
|
||||
nsISocketTransport *aTransport)
|
||||
{
|
||||
// This is a necko only interface, so we can enforce version
|
||||
// requests as a precondition
|
||||
MOZ_ASSERT(version == HTTP_VERSION_2,
|
||||
"Unsupported spdy version");
|
||||
|
||||
// Don't do a runtime check of IsSpdyV?Enabled() here because pref value
|
||||
// may have changed since starting negotiation. The selected protocol comes
|
||||
// from a list provided in the SERVER HELLO filtered by our acceptable
|
||||
// versions, so there is no risk of the server ignoring our prefs.
|
||||
|
||||
Telemetry::Accumulate(Telemetry::SPDY_VERSION2, version);
|
||||
|
||||
return new Http2Session(aTransport, version);
|
||||
}
|
||||
|
||||
SpdyInformation::SpdyInformation()
|
||||
{
|
||||
// highest index of enabled protocols is the
|
||||
// most preferred for ALPN negotiaton
|
||||
Version[0] = HTTP_VERSION_2;
|
||||
VersionString[0] = NS_LITERAL_CSTRING("h2");
|
||||
ALPNCallbacks[0] = Http2Session::ALPNCallback;
|
||||
}
|
||||
|
||||
bool
|
||||
SpdyInformation::ProtocolEnabled(uint32_t index) const
|
||||
{
|
||||
MOZ_ASSERT(index < kCount, "index out of range");
|
||||
|
||||
return gHttpHandler->IsHttp2Enabled();
|
||||
}
|
||||
|
||||
nsresult
|
||||
SpdyInformation::GetNPNIndex(const nsACString &npnString,
|
||||
uint32_t *result) const
|
||||
{
|
||||
if (npnString.IsEmpty())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
for (uint32_t index = 0; index < kCount; ++index) {
|
||||
if (npnString.Equals(VersionString[index])) {
|
||||
*result = index;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// SpdyPushCache
|
||||
//////////////////////////////////////////
|
||||
|
||||
SpdyPushCache::SpdyPushCache()
|
||||
{
|
||||
}
|
||||
|
||||
SpdyPushCache::~SpdyPushCache()
|
||||
{
|
||||
mHashHttp2.Clear();
|
||||
}
|
||||
|
||||
bool
|
||||
SpdyPushCache::RegisterPushedStreamHttp2(nsCString key,
|
||||
Http2PushedStream *stream)
|
||||
{
|
||||
LOG3(("SpdyPushCache::RegisterPushedStreamHttp2 %s 0x%X\n",
|
||||
key.get(), stream->StreamID()));
|
||||
if(mHashHttp2.Get(key)) {
|
||||
LOG3(("SpdyPushCache::RegisterPushedStreamHttp2 %s 0x%X duplicate key\n",
|
||||
key.get(), stream->StreamID()));
|
||||
return false;
|
||||
}
|
||||
mHashHttp2.Put(key, stream);
|
||||
return true;
|
||||
}
|
||||
|
||||
Http2PushedStream *
|
||||
SpdyPushCache::RemovePushedStreamHttp2(nsCString key)
|
||||
{
|
||||
Http2PushedStream *rv = mHashHttp2.Get(key);
|
||||
LOG3(("SpdyPushCache::RemovePushedStreamHttp2 %s 0x%X\n",
|
||||
key.get(), rv ? rv->StreamID() : 0));
|
||||
if (rv)
|
||||
mHashHttp2.Remove(key);
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
120
netwerk/protocol/http/ASpdySession.h
Normal file
120
netwerk/protocol/http/ASpdySession.h
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
/* 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_ASpdySession_h
|
||||
#define mozilla_net_ASpdySession_h
|
||||
|
||||
#include "nsAHttpTransaction.h"
|
||||
#include "prinrval.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsISocketTransport;
|
||||
|
||||
namespace mozilla { namespace net {
|
||||
|
||||
class ASpdySession : public nsAHttpTransaction
|
||||
{
|
||||
public:
|
||||
ASpdySession();
|
||||
virtual ~ASpdySession();
|
||||
|
||||
virtual bool AddStream(nsAHttpTransaction *, int32_t,
|
||||
bool, nsIInterfaceRequestor *) = 0;
|
||||
virtual bool CanReuse() = 0;
|
||||
virtual bool RoomForMoreStreams() = 0;
|
||||
virtual PRIntervalTime IdleTime() = 0;
|
||||
virtual uint32_t ReadTimeoutTick(PRIntervalTime now) = 0;
|
||||
virtual void DontReuse() = 0;
|
||||
|
||||
static ASpdySession *NewSpdySession(uint32_t version, nsISocketTransport *);
|
||||
|
||||
// MaybeReTunnel() is called by the connection manager when it cannot
|
||||
// dispatch a tunneled transaction. That might be because the tunnels it
|
||||
// expects to see are dead (and we may or may not be able to make more),
|
||||
// or it might just need to wait longer for one of them to become free.
|
||||
//
|
||||
// return true if the session takes back ownership of the transaction from
|
||||
// the connection manager.
|
||||
virtual bool MaybeReTunnel(nsAHttpTransaction *) = 0;
|
||||
|
||||
virtual void PrintDiagnostics (nsCString &log) = 0;
|
||||
|
||||
bool ResponseTimeoutEnabled() const override final {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void SendPing() = 0;
|
||||
|
||||
const static uint32_t kSendingChunkSize = 4095;
|
||||
const static uint32_t kTCPSendBufferSize = 131072;
|
||||
|
||||
// This is roughly the amount of data a suspended channel will have to
|
||||
// buffer before h2 flow control kicks in.
|
||||
const static uint32_t kInitialRwin = 12 * 1024 * 1024; // 12MB
|
||||
|
||||
const static uint32_t kDefaultMaxConcurrent = 100;
|
||||
|
||||
// soft errors are errors that terminate a stream without terminating the
|
||||
// connection. In general non-network errors are stream errors as well
|
||||
// as network specific items like cancels.
|
||||
bool SoftStreamError(nsresult code)
|
||||
{
|
||||
if (NS_SUCCEEDED(code) || code == NS_BASE_STREAM_WOULD_BLOCK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// this could go either way, but because there are network instances of
|
||||
// it being a hard error we should consider it hard.
|
||||
if (code == NS_ERROR_FAILURE || code == NS_ERROR_OUT_OF_MEMORY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NS_ERROR_GET_MODULE(code) != NS_ERROR_MODULE_NETWORK) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// these are network specific soft errors
|
||||
return (code == NS_BASE_STREAM_CLOSED || code == NS_BINDING_FAILED ||
|
||||
code == NS_BINDING_ABORTED || code == NS_BINDING_REDIRECTED ||
|
||||
code == NS_ERROR_INVALID_CONTENT_ENCODING ||
|
||||
code == NS_BINDING_RETARGETED || code == NS_ERROR_CORRUPTED_CONTENT);
|
||||
}
|
||||
};
|
||||
|
||||
typedef bool (*ALPNCallback) (nsISupports *); // nsISSLSocketControl is typical
|
||||
|
||||
// this is essentially a single instantiation as a member of nsHttpHandler.
|
||||
// It could be all static except using static ctors of XPCOM objects is a
|
||||
// bad idea.
|
||||
class SpdyInformation
|
||||
{
|
||||
public:
|
||||
SpdyInformation();
|
||||
~SpdyInformation() {}
|
||||
|
||||
static const uint32_t kCount = 1;
|
||||
|
||||
// determine the index (0..kCount-1) of the spdy information that
|
||||
// correlates to the npn string. NS_FAILED() if no match is found.
|
||||
nsresult GetNPNIndex(const nsACString &npnString, uint32_t *result) const;
|
||||
|
||||
// determine if a version of the protocol is enabled for index < kCount
|
||||
bool ProtocolEnabled(uint32_t index) const;
|
||||
|
||||
uint8_t Version[kCount]; // telemetry enum e.g. SPDY_VERSION_31
|
||||
nsCString VersionString[kCount]; // npn string e.g. "spdy/3.1"
|
||||
|
||||
// the ALPNCallback function allows the protocol stack to decide whether or
|
||||
// not to offer a particular protocol based on the known TLS information
|
||||
// that we will offer in the client hello (such as version). There has
|
||||
// not been a Server Hello received yet, so not much else can be considered.
|
||||
ALPNCallback ALPNCallbacks[kCount];
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_ASpdySession_h
|
||||
151
netwerk/protocol/http/AltDataOutputStreamChild.cpp
Normal file
151
netwerk/protocol/http/AltDataOutputStreamChild.cpp
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
#include "mozilla/net/AltDataOutputStreamChild.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsIInputStream.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
NS_IMPL_ADDREF(AltDataOutputStreamChild)
|
||||
|
||||
NS_IMETHODIMP_(MozExternalRefCountType) AltDataOutputStreamChild::Release()
|
||||
{
|
||||
NS_PRECONDITION(0 != mRefCnt, "dup release");
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
|
||||
--mRefCnt;
|
||||
NS_LOG_RELEASE(this, mRefCnt, "AltDataOutputStreamChild");
|
||||
|
||||
if (mRefCnt == 1 && mIPCOpen) {
|
||||
// Send_delete calls NeckoChild::PAltDataOutputStreamChild, which will release
|
||||
// again to refcount == 0
|
||||
PAltDataOutputStreamChild::Send__delete__(this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mRefCnt == 0) {
|
||||
mRefCnt = 1; /* stabilize */
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
return mRefCnt;
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(AltDataOutputStreamChild)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
AltDataOutputStreamChild::AltDataOutputStreamChild()
|
||||
: mIPCOpen(false)
|
||||
, mError(NS_OK)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
|
||||
}
|
||||
|
||||
AltDataOutputStreamChild::~AltDataOutputStreamChild()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
AltDataOutputStreamChild::AddIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(!mIPCOpen, "Attempt to retain more than one IPDL reference");
|
||||
mIPCOpen = true;
|
||||
AddRef();
|
||||
}
|
||||
|
||||
void
|
||||
AltDataOutputStreamChild::ReleaseIPDLReference()
|
||||
{
|
||||
MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference");
|
||||
mIPCOpen = false;
|
||||
Release();
|
||||
}
|
||||
|
||||
bool
|
||||
AltDataOutputStreamChild::WriteDataInChunks(const nsCString& data)
|
||||
{
|
||||
const uint32_t kChunkSize = 128*1024;
|
||||
uint32_t next = std::min(data.Length(), kChunkSize);
|
||||
for (uint32_t i = 0; i < data.Length();
|
||||
i = next, next = std::min(data.Length(), next + kChunkSize)) {
|
||||
nsCString chunk(Substring(data, i, kChunkSize));
|
||||
if (mIPCOpen && !SendWriteData(chunk)) {
|
||||
mIPCOpen = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AltDataOutputStreamChild::Close()
|
||||
{
|
||||
if (!mIPCOpen) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
if (NS_FAILED(mError)) {
|
||||
return mError;
|
||||
}
|
||||
Unused << SendClose();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AltDataOutputStreamChild::Flush()
|
||||
{
|
||||
if (!mIPCOpen) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
if (NS_FAILED(mError)) {
|
||||
return mError;
|
||||
}
|
||||
|
||||
// This is a no-op
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AltDataOutputStreamChild::Write(const char * aBuf, uint32_t aCount, uint32_t *_retval)
|
||||
{
|
||||
if (!mIPCOpen) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
if (NS_FAILED(mError)) {
|
||||
return mError;
|
||||
}
|
||||
if (WriteDataInChunks(nsCString(aBuf, aCount))) {
|
||||
*_retval = aCount;
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AltDataOutputStreamChild::WriteFrom(nsIInputStream *aFromStream, uint32_t aCount, uint32_t *_retval)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AltDataOutputStreamChild::WriteSegments(nsReadSegmentFun aReader, void *aClosure, uint32_t aCount, uint32_t *_retval)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AltDataOutputStreamChild::IsNonBlocking(bool *_retval)
|
||||
{
|
||||
*_retval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
AltDataOutputStreamChild::RecvError(const nsresult& err)
|
||||
{
|
||||
mError = err;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
46
netwerk/protocol/http/AltDataOutputStreamChild.h
Normal file
46
netwerk/protocol/http/AltDataOutputStreamChild.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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_AltDataOutputStreamChild_h
|
||||
#define mozilla_net_AltDataOutputStreamChild_h
|
||||
|
||||
#include "mozilla/net/PAltDataOutputStreamChild.h"
|
||||
#include "nsIOutputStream.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class AltDataOutputStreamChild
|
||||
: public PAltDataOutputStreamChild
|
||||
, public nsIOutputStream
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOUTPUTSTREAM
|
||||
explicit AltDataOutputStreamChild();
|
||||
|
||||
void AddIPDLReference();
|
||||
void ReleaseIPDLReference();
|
||||
// Saves an error code which will be reported to the writer on the next call.
|
||||
virtual bool RecvError(const nsresult& err) override;
|
||||
|
||||
private:
|
||||
virtual ~AltDataOutputStreamChild();
|
||||
// Sends data to the parent process in 256k chunks.
|
||||
bool WriteDataInChunks(const nsCString& data);
|
||||
|
||||
bool mIPCOpen;
|
||||
// If there was an error opening the output stream or writing to it on the
|
||||
// parent side, this will be set to the error code. We check it before we
|
||||
// write so we can report an error to the consumer.
|
||||
nsresult mError;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_AltDataOutputStreamChild_h
|
||||
71
netwerk/protocol/http/AltDataOutputStreamParent.cpp
Normal file
71
netwerk/protocol/http/AltDataOutputStreamParent.cpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/. */
|
||||
|
||||
#include "mozilla/net/AltDataOutputStreamParent.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
NS_IMPL_ISUPPORTS0(AltDataOutputStreamParent)
|
||||
|
||||
AltDataOutputStreamParent::AltDataOutputStreamParent(nsIOutputStream* aStream)
|
||||
: mOutputStream(aStream)
|
||||
, mStatus(NS_OK)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
|
||||
}
|
||||
|
||||
AltDataOutputStreamParent::~AltDataOutputStreamParent()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
|
||||
}
|
||||
|
||||
bool
|
||||
AltDataOutputStreamParent::RecvWriteData(const nsCString& data)
|
||||
{
|
||||
if (NS_FAILED(mStatus)) {
|
||||
Unused << SendError(mStatus);
|
||||
return true;
|
||||
}
|
||||
nsresult rv;
|
||||
uint32_t n;
|
||||
if (mOutputStream) {
|
||||
rv = mOutputStream->Write(data.BeginReading(), data.Length(), &n);
|
||||
MOZ_ASSERT(n == data.Length());
|
||||
if (NS_FAILED(rv)) {
|
||||
Unused << SendError(rv);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AltDataOutputStreamParent::RecvClose()
|
||||
{
|
||||
if (NS_FAILED(mStatus)) {
|
||||
Unused << SendError(mStatus);
|
||||
return true;
|
||||
}
|
||||
nsresult rv;
|
||||
if (mOutputStream) {
|
||||
rv = mOutputStream->Close();
|
||||
if (NS_FAILED(rv)) {
|
||||
Unused << SendError(rv);
|
||||
}
|
||||
mOutputStream = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
AltDataOutputStreamParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
52
netwerk/protocol/http/AltDataOutputStreamParent.h
Normal file
52
netwerk/protocol/http/AltDataOutputStreamParent.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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_AltDataOutputStreamParent_h
|
||||
#define mozilla_net_AltDataOutputStreamParent_h
|
||||
|
||||
#include "mozilla/net/PAltDataOutputStreamParent.h"
|
||||
#include "nsIOutputStream.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
// Forwards data received from the content process to an output stream.
|
||||
class AltDataOutputStreamParent
|
||||
: public PAltDataOutputStreamParent
|
||||
, public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// Called from NeckoParent::AllocPAltDataOutputStreamParent which also opens
|
||||
// the output stream.
|
||||
// aStream may be null
|
||||
explicit AltDataOutputStreamParent(nsIOutputStream* aStream);
|
||||
|
||||
// Called when data is received from the content process.
|
||||
// We proceed to write that data to the output stream.
|
||||
virtual bool RecvWriteData(const nsCString& data) override;
|
||||
// Called when AltDataOutputStreamChild::Close() is
|
||||
// Closes and nulls the output stream.
|
||||
virtual bool RecvClose() override;
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
// Sets an error that will be reported to the content process.
|
||||
void SetError(nsresult status) { mStatus = status; }
|
||||
|
||||
private:
|
||||
virtual ~AltDataOutputStreamParent();
|
||||
nsCOMPtr<nsIOutputStream> mOutputStream;
|
||||
// In case any error occurs mStatus will be != NS_OK, and this status code will
|
||||
// be sent to the content process asynchronously.
|
||||
nsresult mStatus;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_AltDataOutputStreamParent_h
|
||||
1075
netwerk/protocol/http/AlternateServices.cpp
Normal file
1075
netwerk/protocol/http/AlternateServices.cpp
Normal file
File diff suppressed because it is too large
Load diff
191
netwerk/protocol/http/AlternateServices.h
Normal file
191
netwerk/protocol/http/AlternateServices.h
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/. */
|
||||
|
||||
/*
|
||||
Alt-Svc allows separation of transport routing from the origin host without
|
||||
using a proxy. See https://httpwg.github.io/http-extensions/alt-svc.html and
|
||||
https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-06
|
||||
|
||||
Nice To Have Future Enhancements::
|
||||
* flush on network change event when we have an indicator
|
||||
* use established https channel for http instead separate of conninfo hash
|
||||
* pin via http-tls header
|
||||
* clear based on origin when a random fail happens not just 421
|
||||
* upon establishment of channel, cancel and retry trans that have not yet written anything
|
||||
* persistent storage (including private browsing filter)
|
||||
* memory reporter for cache, but this is rather tiny
|
||||
*/
|
||||
|
||||
#ifndef mozilla_net_AlternateServices_h
|
||||
#define mozilla_net_AlternateServices_h
|
||||
|
||||
#include "mozilla/DataStorage.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsISpeculativeConnect.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
|
||||
class nsILoadInfo;
|
||||
|
||||
namespace mozilla { namespace net {
|
||||
|
||||
class nsProxyInfo;
|
||||
class nsHttpConnectionInfo;
|
||||
class nsHttpTransaction;
|
||||
class nsHttpChannel;
|
||||
class WellKnownChecker;
|
||||
|
||||
class AltSvcMapping
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AltSvcMapping)
|
||||
|
||||
private: // ctor from ProcessHeader
|
||||
AltSvcMapping(DataStorage *storage,
|
||||
int32_t storageEpoch,
|
||||
const nsACString &originScheme,
|
||||
const nsACString &originHost,
|
||||
int32_t originPort,
|
||||
const nsACString &username,
|
||||
bool privateBrowsing,
|
||||
uint32_t expiresAt,
|
||||
const nsACString &alternateHost,
|
||||
int32_t alternatePort,
|
||||
const nsACString &npnToken);
|
||||
public:
|
||||
AltSvcMapping(DataStorage *storage, int32_t storageEpoch, const nsCString &serialized);
|
||||
|
||||
static void ProcessHeader(const nsCString &buf, const nsCString &originScheme,
|
||||
const nsCString &originHost, int32_t originPort,
|
||||
const nsACString &username, bool privateBrowsing,
|
||||
nsIInterfaceRequestor *callbacks, nsProxyInfo *proxyInfo,
|
||||
uint32_t caps, const NeckoOriginAttributes &originAttributes);
|
||||
|
||||
const nsCString &AlternateHost() const { return mAlternateHost; }
|
||||
const nsCString &OriginHost() const { return mOriginHost; }
|
||||
uint32_t OriginPort() const { return mOriginPort; }
|
||||
const nsCString &HashKey() const { return mHashKey; }
|
||||
uint32_t AlternatePort() const { return mAlternatePort; }
|
||||
bool Validated() { return mValidated; }
|
||||
int32_t GetExpiresAt() { return mExpiresAt; }
|
||||
bool RouteEquals(AltSvcMapping *map);
|
||||
bool HTTPS() { return mHttps; }
|
||||
|
||||
void GetConnectionInfo(nsHttpConnectionInfo **outCI, nsProxyInfo *pi,
|
||||
const NeckoOriginAttributes &originAttributes);
|
||||
|
||||
int32_t TTL();
|
||||
int32_t StorageEpoch() { return mStorageEpoch; }
|
||||
bool Private() { return mPrivate; }
|
||||
|
||||
void SetValidated(bool val);
|
||||
void SetMixedScheme(bool val);
|
||||
void SetExpiresAt(int32_t val);
|
||||
void SetExpired();
|
||||
void Sync();
|
||||
|
||||
static void MakeHashKey(nsCString &outKey,
|
||||
const nsACString &originScheme,
|
||||
const nsACString &originHost,
|
||||
int32_t originPort,
|
||||
bool privateBrowsing);
|
||||
|
||||
private:
|
||||
virtual ~AltSvcMapping() {};
|
||||
void SyncString(nsCString val);
|
||||
RefPtr<DataStorage> mStorage;
|
||||
int32_t mStorageEpoch;
|
||||
void Serialize (nsCString &out);
|
||||
|
||||
nsCString mHashKey;
|
||||
|
||||
// If you change any of these members, update Serialize()
|
||||
nsCString mAlternateHost;
|
||||
MOZ_INIT_OUTSIDE_CTOR int32_t mAlternatePort;
|
||||
|
||||
nsCString mOriginHost;
|
||||
MOZ_INIT_OUTSIDE_CTOR int32_t mOriginPort;
|
||||
|
||||
nsCString mUsername;
|
||||
MOZ_INIT_OUTSIDE_CTOR bool mPrivate;
|
||||
|
||||
MOZ_INIT_OUTSIDE_CTOR uint32_t mExpiresAt; // alt-svc mappping
|
||||
|
||||
MOZ_INIT_OUTSIDE_CTOR bool mValidated;
|
||||
MOZ_INIT_OUTSIDE_CTOR bool mHttps; // origin is https://
|
||||
MOZ_INIT_OUTSIDE_CTOR bool mMixedScheme; // .wk allows http and https on same con
|
||||
|
||||
nsCString mNPNToken;
|
||||
};
|
||||
|
||||
class AltSvcOverride : public nsIInterfaceRequestor
|
||||
, public nsISpeculativeConnectionOverrider
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSISPECULATIVECONNECTIONOVERRIDER
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
explicit AltSvcOverride(nsIInterfaceRequestor *aRequestor)
|
||||
: mCallbacks(aRequestor) {}
|
||||
|
||||
private:
|
||||
virtual ~AltSvcOverride() {}
|
||||
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
|
||||
};
|
||||
|
||||
class TransactionObserver : public nsIStreamListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
|
||||
TransactionObserver(nsHttpChannel *channel, WellKnownChecker *checker);
|
||||
void Complete(nsHttpTransaction *, nsresult);
|
||||
private:
|
||||
friend class WellKnownChecker;
|
||||
virtual ~TransactionObserver() {}
|
||||
|
||||
nsCOMPtr<nsISupports> mChannelRef;
|
||||
nsHttpChannel *mChannel;
|
||||
WellKnownChecker *mChecker;
|
||||
nsCString mWKResponse;
|
||||
|
||||
bool mRanOnce;
|
||||
bool mAuthOK; // confirmed no TLS failure
|
||||
bool mVersionOK; // connection h2
|
||||
bool mStatusOK; // HTTP Status 200
|
||||
};
|
||||
|
||||
class AltSvcCache
|
||||
{
|
||||
public:
|
||||
AltSvcCache() : mStorageEpoch(0) {}
|
||||
virtual ~AltSvcCache () {};
|
||||
void UpdateAltServiceMapping(AltSvcMapping *map, nsProxyInfo *pi,
|
||||
nsIInterfaceRequestor *, uint32_t caps,
|
||||
const NeckoOriginAttributes &originAttributes); // main thread
|
||||
already_AddRefed<AltSvcMapping> GetAltServiceMapping(const nsACString &scheme,
|
||||
const nsACString &host,
|
||||
int32_t port, bool pb);
|
||||
void ClearAltServiceMappings();
|
||||
void ClearHostMapping(const nsACString &host, int32_t port);
|
||||
void ClearHostMapping(nsHttpConnectionInfo *ci);
|
||||
DataStorage *GetStoragePtr() { return mStorage.get(); }
|
||||
int32_t StorageEpoch() { return mStorageEpoch; }
|
||||
|
||||
private:
|
||||
already_AddRefed<AltSvcMapping> LookupMapping(const nsCString &key, bool privateBrowsing);
|
||||
RefPtr<DataStorage> mStorage;
|
||||
int32_t mStorageEpoch;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // include guard
|
||||
123
netwerk/protocol/http/CacheControlParser.cpp
Normal file
123
netwerk/protocol/http/CacheControlParser.cpp
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/. */
|
||||
|
||||
#include "CacheControlParser.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
CacheControlParser::CacheControlParser(nsACString const &aHeader)
|
||||
: Tokenizer(aHeader, nullptr, "-_")
|
||||
, mMaxAgeSet(false)
|
||||
, mMaxAge(0)
|
||||
, mMaxStaleSet(false)
|
||||
, mMaxStale(0)
|
||||
, mMinFreshSet(false)
|
||||
, mMinFresh(0)
|
||||
, mNoCache(false)
|
||||
, mNoStore(false)
|
||||
{
|
||||
SkipWhites();
|
||||
if (!CheckEOF()) {
|
||||
Directive();
|
||||
}
|
||||
}
|
||||
|
||||
void CacheControlParser::Directive()
|
||||
{
|
||||
if (CheckWord("no-cache")) {
|
||||
mNoCache = true;
|
||||
IgnoreDirective(); // ignore any optionally added values
|
||||
} else if (CheckWord("no-store")) {
|
||||
mNoStore = true;
|
||||
} else if (CheckWord("max-age")) {
|
||||
mMaxAgeSet = SecondsValue(&mMaxAge);
|
||||
} else if (CheckWord("max-stale")) {
|
||||
mMaxStaleSet = SecondsValue(&mMaxStale, PR_UINT32_MAX);
|
||||
} else if (CheckWord("min-fresh")) {
|
||||
mMinFreshSet = SecondsValue(&mMinFresh);
|
||||
} else {
|
||||
IgnoreDirective();
|
||||
}
|
||||
|
||||
SkipWhites();
|
||||
if (CheckEOF()) {
|
||||
return;
|
||||
}
|
||||
if (CheckChar(',')) {
|
||||
SkipWhites();
|
||||
Directive();
|
||||
return;
|
||||
}
|
||||
|
||||
NS_WARNING("Unexpected input in Cache-control header value");
|
||||
}
|
||||
|
||||
bool CacheControlParser::SecondsValue(uint32_t *seconds, uint32_t defaultVal)
|
||||
{
|
||||
SkipWhites();
|
||||
if (!CheckChar('=')) {
|
||||
*seconds = defaultVal;
|
||||
return !!defaultVal;
|
||||
}
|
||||
|
||||
SkipWhites();
|
||||
if (!ReadInteger(seconds)) {
|
||||
NS_WARNING("Unexpected value in Cache-control header value");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CacheControlParser::IgnoreDirective()
|
||||
{
|
||||
Token t;
|
||||
while (Next(t)) {
|
||||
if (t.Equals(Token::Char(',')) || t.Equals(Token::EndOfFile())) {
|
||||
Rollback();
|
||||
break;
|
||||
}
|
||||
if (t.Equals(Token::Char('"'))) {
|
||||
SkipUntil(Token::Char('"'));
|
||||
if (!CheckChar('"')) {
|
||||
NS_WARNING("Missing quoted string expansion in Cache-control header value");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CacheControlParser::MaxAge(uint32_t *seconds)
|
||||
{
|
||||
*seconds = mMaxAge;
|
||||
return mMaxAgeSet;
|
||||
}
|
||||
|
||||
bool CacheControlParser::MaxStale(uint32_t *seconds)
|
||||
{
|
||||
*seconds = mMaxStale;
|
||||
return mMaxStaleSet;
|
||||
}
|
||||
|
||||
bool CacheControlParser::MinFresh(uint32_t *seconds)
|
||||
{
|
||||
*seconds = mMinFresh;
|
||||
return mMinFreshSet;
|
||||
}
|
||||
|
||||
bool CacheControlParser::NoCache()
|
||||
{
|
||||
return mNoCache;
|
||||
}
|
||||
|
||||
bool CacheControlParser::NoStore()
|
||||
{
|
||||
return mNoStore;
|
||||
}
|
||||
|
||||
} // net
|
||||
} // mozilla
|
||||
44
netwerk/protocol/http/CacheControlParser.h
Normal file
44
netwerk/protocol/http/CacheControlParser.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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 CacheControlParser_h__
|
||||
#define CacheControlParser_h__
|
||||
|
||||
#include "mozilla/Tokenizer.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class CacheControlParser final : Tokenizer
|
||||
{
|
||||
public:
|
||||
explicit CacheControlParser(nsACString const &header);
|
||||
|
||||
bool MaxAge(uint32_t *seconds);
|
||||
bool MaxStale(uint32_t *seconds);
|
||||
bool MinFresh(uint32_t *seconds);
|
||||
bool NoCache();
|
||||
bool NoStore();
|
||||
|
||||
private:
|
||||
void Directive();
|
||||
void IgnoreDirective();
|
||||
bool SecondsValue(uint32_t *seconds, uint32_t defaultVal = 0);
|
||||
|
||||
bool mMaxAgeSet;
|
||||
uint32_t mMaxAge;
|
||||
bool mMaxStaleSet;
|
||||
uint32_t mMaxStale;
|
||||
bool mMinFreshSet;
|
||||
uint32_t mMinFresh;
|
||||
bool mNoCache;
|
||||
bool mNoStore;
|
||||
};
|
||||
|
||||
} // net
|
||||
} // mozilla
|
||||
|
||||
#endif
|
||||
211
netwerk/protocol/http/ConnectionDiagnostics.cpp
Normal file
211
netwerk/protocol/http/ConnectionDiagnostics.cpp
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/. */
|
||||
|
||||
// HttpLog.h should generally be included first
|
||||
#include "HttpLog.h"
|
||||
|
||||
#include "nsHttpConnectionMgr.h"
|
||||
#include "nsHttpConnection.h"
|
||||
#include "Http2Session.h"
|
||||
#include "nsHttpHandler.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsHttpRequestHead.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsSocketTransportService2.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::PrintDiagnostics()
|
||||
{
|
||||
PostEvent(&nsHttpConnectionMgr::OnMsgPrintDiagnostics, 0, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::OnMsgPrintDiagnostics(int32_t, ARefBase *)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
|
||||
nsCOMPtr<nsIConsoleService> consoleService =
|
||||
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
|
||||
if (!consoleService)
|
||||
return;
|
||||
|
||||
mLogData.AppendPrintf("HTTP Connection Diagnostics\n---------------------\n");
|
||||
mLogData.AppendPrintf("IsSpdyEnabled() = %d\n", gHttpHandler->IsSpdyEnabled());
|
||||
mLogData.AppendPrintf("MaxSocketCount() = %d\n", gHttpHandler->MaxSocketCount());
|
||||
mLogData.AppendPrintf("mNumActiveConns = %d\n", mNumActiveConns);
|
||||
mLogData.AppendPrintf("mNumIdleConns = %d\n", mNumIdleConns);
|
||||
|
||||
for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsAutoPtr<nsConnectionEntry>& ent = iter.Data();
|
||||
|
||||
mLogData.AppendPrintf(" ent host = %s hashkey = %s\n",
|
||||
ent->mConnInfo->Origin(), ent->mConnInfo->HashKey().get());
|
||||
mLogData.AppendPrintf(" AtActiveConnectionLimit = %d\n",
|
||||
AtActiveConnectionLimit(ent, NS_HTTP_ALLOW_KEEPALIVE));
|
||||
mLogData.AppendPrintf(" RestrictConnections = %d\n",
|
||||
RestrictConnections(ent));
|
||||
mLogData.AppendPrintf(" Pending Q Length = %u\n",
|
||||
ent->mPendingQ.Length());
|
||||
mLogData.AppendPrintf(" Active Conns Length = %u\n",
|
||||
ent->mActiveConns.Length());
|
||||
mLogData.AppendPrintf(" Idle Conns Length = %u\n",
|
||||
ent->mIdleConns.Length());
|
||||
mLogData.AppendPrintf(" Half Opens Length = %u\n",
|
||||
ent->mHalfOpens.Length());
|
||||
mLogData.AppendPrintf(" Coalescing Keys Length = %u\n",
|
||||
ent->mCoalescingKeys.Length());
|
||||
mLogData.AppendPrintf(" Spdy using = %d, preferred = %d\n",
|
||||
ent->mUsingSpdy, ent->mInPreferredHash);
|
||||
mLogData.AppendPrintf(" pipelinestate = %d penalty = %d\n",
|
||||
ent->mPipelineState, ent->mPipeliningPenalty);
|
||||
|
||||
uint32_t i;
|
||||
for (i = 0; i < nsAHttpTransaction::CLASS_MAX; ++i) {
|
||||
mLogData.AppendPrintf(" pipeline per class penalty 0x%x %d\n",
|
||||
i, ent->mPipeliningClassPenalty[i]);
|
||||
}
|
||||
for (i = 0; i < ent->mActiveConns.Length(); ++i) {
|
||||
mLogData.AppendPrintf(" :: Active Connection #%u\n", i);
|
||||
ent->mActiveConns[i]->PrintDiagnostics(mLogData);
|
||||
}
|
||||
for (i = 0; i < ent->mIdleConns.Length(); ++i) {
|
||||
mLogData.AppendPrintf(" :: Idle Connection #%u\n", i);
|
||||
ent->mIdleConns[i]->PrintDiagnostics(mLogData);
|
||||
}
|
||||
for (i = 0; i < ent->mHalfOpens.Length(); ++i) {
|
||||
mLogData.AppendPrintf(" :: Half Open #%u\n", i);
|
||||
ent->mHalfOpens[i]->PrintDiagnostics(mLogData);
|
||||
}
|
||||
for (i = 0; i < ent->mPendingQ.Length(); ++i) {
|
||||
mLogData.AppendPrintf(" :: Pending Transaction #%u\n", i);
|
||||
ent->mPendingQ[i]->PrintDiagnostics(mLogData);
|
||||
}
|
||||
for (i = 0; i < ent->mCoalescingKeys.Length(); ++i) {
|
||||
mLogData.AppendPrintf(" :: Coalescing Key #%u %s\n",
|
||||
i, ent->mCoalescingKeys[i].get());
|
||||
}
|
||||
}
|
||||
|
||||
consoleService->LogStringMessage(NS_ConvertUTF8toUTF16(mLogData).Data());
|
||||
mLogData.Truncate();
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::nsHalfOpenSocket::PrintDiagnostics(nsCString &log)
|
||||
{
|
||||
log.AppendPrintf(" has connected = %d, isSpeculative = %d\n",
|
||||
HasConnected(), IsSpeculative());
|
||||
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
|
||||
if (mPrimarySynStarted.IsNull())
|
||||
log.AppendPrintf(" primary not started\n");
|
||||
else
|
||||
log.AppendPrintf(" primary started %.2fms ago\n",
|
||||
(now - mPrimarySynStarted).ToMilliseconds());
|
||||
|
||||
if (mBackupSynStarted.IsNull())
|
||||
log.AppendPrintf(" backup not started\n");
|
||||
else
|
||||
log.AppendPrintf(" backup started %.2f ago\n",
|
||||
(now - mBackupSynStarted).ToMilliseconds());
|
||||
|
||||
log.AppendPrintf(" primary transport %d, backup transport %d\n",
|
||||
!!mSocketTransport.get(), !!mBackupTransport.get());
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnection::PrintDiagnostics(nsCString &log)
|
||||
{
|
||||
log.AppendPrintf(" CanDirectlyActivate = %d\n", CanDirectlyActivate());
|
||||
|
||||
log.AppendPrintf(" npncomplete = %d setupSSLCalled = %d\n",
|
||||
mNPNComplete, mSetupSSLCalled);
|
||||
|
||||
log.AppendPrintf(" spdyVersion = %d reportedSpdy = %d everspdy = %d\n",
|
||||
mUsingSpdyVersion, mReportedSpdy, mEverUsedSpdy);
|
||||
|
||||
log.AppendPrintf(" iskeepalive = %d dontReuse = %d isReused = %d\n",
|
||||
IsKeepAlive(), mDontReuse, mIsReused);
|
||||
|
||||
log.AppendPrintf(" mTransaction = %d mSpdySession = %d\n",
|
||||
!!mTransaction.get(), !!mSpdySession.get());
|
||||
|
||||
PRIntervalTime now = PR_IntervalNow();
|
||||
log.AppendPrintf(" time since last read = %ums\n",
|
||||
PR_IntervalToMilliseconds(now - mLastReadTime));
|
||||
|
||||
log.AppendPrintf(" max-read/read/written %lld/%lld/%lld\n",
|
||||
mMaxBytesRead, mTotalBytesRead, mTotalBytesWritten);
|
||||
|
||||
log.AppendPrintf(" rtt = %ums\n", PR_IntervalToMilliseconds(mRtt));
|
||||
|
||||
log.AppendPrintf(" idlemonitoring = %d transactionCount=%d\n",
|
||||
mIdleMonitoring, mHttp1xTransactionCount);
|
||||
|
||||
log.AppendPrintf(" supports pipeline = %d classification = 0x%x\n",
|
||||
mSupportsPipelining, mClassification);
|
||||
|
||||
if (mSpdySession)
|
||||
mSpdySession->PrintDiagnostics(log);
|
||||
}
|
||||
|
||||
void
|
||||
Http2Session::PrintDiagnostics(nsCString &log)
|
||||
{
|
||||
log.AppendPrintf(" ::: HTTP2\n");
|
||||
log.AppendPrintf(" shouldgoaway = %d mClosed = %d CanReuse = %d nextID=0x%X\n",
|
||||
mShouldGoAway, mClosed, CanReuse(), mNextStreamID);
|
||||
|
||||
log.AppendPrintf(" concurrent = %d maxconcurrent = %d\n",
|
||||
mConcurrent, mMaxConcurrent);
|
||||
|
||||
log.AppendPrintf(" roomformorestreams = %d roomformoreconcurrent = %d\n",
|
||||
RoomForMoreStreams(), RoomForMoreConcurrent());
|
||||
|
||||
log.AppendPrintf(" transactionHashCount = %d streamIDHashCount = %d\n",
|
||||
mStreamTransactionHash.Count(),
|
||||
mStreamIDHash.Count());
|
||||
|
||||
log.AppendPrintf(" Queued Stream Size = %d\n", mQueuedStreams.GetSize());
|
||||
|
||||
PRIntervalTime now = PR_IntervalNow();
|
||||
log.AppendPrintf(" Ping Threshold = %ums\n",
|
||||
PR_IntervalToMilliseconds(mPingThreshold));
|
||||
log.AppendPrintf(" Ping Timeout = %ums\n",
|
||||
PR_IntervalToMilliseconds(gHttpHandler->SpdyPingTimeout()));
|
||||
log.AppendPrintf(" Idle for Any Activity (ping) = %ums\n",
|
||||
PR_IntervalToMilliseconds(now - mLastReadEpoch));
|
||||
log.AppendPrintf(" Idle for Data Activity = %ums\n",
|
||||
PR_IntervalToMilliseconds(now - mLastDataReadEpoch));
|
||||
if (mPingSentEpoch)
|
||||
log.AppendPrintf(" Ping Outstanding (ping) = %ums, expired = %d\n",
|
||||
PR_IntervalToMilliseconds(now - mPingSentEpoch),
|
||||
now - mPingSentEpoch >= gHttpHandler->SpdyPingTimeout());
|
||||
else
|
||||
log.AppendPrintf(" No Ping Outstanding\n");
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpTransaction::PrintDiagnostics(nsCString &log)
|
||||
{
|
||||
if (!mRequestHead)
|
||||
return;
|
||||
|
||||
nsAutoCString requestURI;
|
||||
mRequestHead->RequestURI(requestURI);
|
||||
log.AppendPrintf(" ::: uri = %s\n", requestURI.get());
|
||||
log.AppendPrintf(" caps = 0x%x\n", mCaps);
|
||||
log.AppendPrintf(" priority = %d\n", mPriority);
|
||||
log.AppendPrintf(" restart count = %u\n", mRestartCount);
|
||||
log.AppendPrintf(" classification = 0x%x\n", mClassification);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
273
netwerk/protocol/http/HSTSPrimerListener.cpp
Normal file
273
netwerk/protocol/http/HSTSPrimerListener.cpp
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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/. */
|
||||
|
||||
#include "nsHttp.h"
|
||||
|
||||
#include "HSTSPrimerListener.h"
|
||||
#include "nsIHstsPrimingCallback.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsSecurityHeaderParser.h"
|
||||
#include "nsISiteSecurityService.h"
|
||||
#include "nsISocketProvider.h"
|
||||
#include "nsISSLStatus.h"
|
||||
#include "nsISSLStatusProvider.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsHttpChannel.h"
|
||||
#include "LoadInfo.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
NS_IMPL_ISUPPORTS(HSTSPrimingListener, nsIStreamListener,
|
||||
nsIRequestObserver, nsIInterfaceRequestor)
|
||||
|
||||
NS_IMETHODIMP
|
||||
HSTSPrimingListener::GetInterface(const nsIID & aIID, void **aResult)
|
||||
{
|
||||
return QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HSTSPrimingListener::OnStartRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext)
|
||||
{
|
||||
nsresult primingResult = CheckHSTSPrimingRequestStatus(aRequest);
|
||||
nsCOMPtr<nsIHstsPrimingCallback> callback(mCallback);
|
||||
mCallback = nullptr;
|
||||
|
||||
nsCOMPtr<nsITimedChannel> timingChannel =
|
||||
do_QueryInterface(callback);
|
||||
if (timingChannel) {
|
||||
TimeStamp channelCreationTime;
|
||||
nsresult rv = timingChannel->GetChannelCreation(&channelCreationTime);
|
||||
if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
|
||||
PRUint32 interval =
|
||||
(PRUint32) (TimeStamp::Now() - channelCreationTime).ToMilliseconds();
|
||||
Telemetry::Accumulate(Telemetry::HSTS_PRIMING_REQUEST_DURATION,
|
||||
(NS_SUCCEEDED(primingResult)) ? NS_LITERAL_CSTRING("success")
|
||||
: NS_LITERAL_CSTRING("failure"),
|
||||
interval);
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_FAILED(primingResult)) {
|
||||
LOG(("HSTS Priming Failed (request was not approved)"));
|
||||
return callback->OnHSTSPrimingFailed(primingResult, false);
|
||||
}
|
||||
|
||||
LOG(("HSTS Priming Succeeded (request was approved)"));
|
||||
return callback->OnHSTSPrimingSucceeded(false);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HSTSPrimingListener::OnStopRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsresult aStatus)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HSTSPrimingListener::CheckHSTSPrimingRequestStatus(nsIRequest* aRequest)
|
||||
{
|
||||
nsresult status;
|
||||
nsresult rv = aRequest->GetStatus(&status);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (NS_FAILED(status)) {
|
||||
return NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
|
||||
// Test that things worked on a HTTP level
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
|
||||
NS_ENSURE_STATE(httpChannel);
|
||||
nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aRequest);
|
||||
NS_ENSURE_STATE(internal);
|
||||
|
||||
bool succeedded;
|
||||
rv = httpChannel->GetRequestSucceeded(&succeedded);
|
||||
if (NS_FAILED(rv) || !succeedded) {
|
||||
// If the request did not return a 2XX response, don't process it
|
||||
return NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
|
||||
bool synthesized = false;
|
||||
nsHttpChannel* rawHttpChannel = static_cast<nsHttpChannel*>(httpChannel.get());
|
||||
rv = rawHttpChannel->GetResponseSynthesized(&synthesized);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (synthesized) {
|
||||
// Don't consider synthesized responses
|
||||
return NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
|
||||
// check to see if the HSTS cache was updated
|
||||
nsCOMPtr<nsISiteSecurityService> sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = httpChannel->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(uri, NS_ERROR_CONTENT_BLOCKED);
|
||||
|
||||
bool hsts;
|
||||
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, 0, nullptr, &hsts);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (hsts) {
|
||||
// An HSTS upgrade was found
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// There is no HSTS upgrade available
|
||||
return NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
|
||||
/** nsIStreamListener methods **/
|
||||
|
||||
NS_IMETHODIMP
|
||||
HSTSPrimingListener::OnDataAvailable(nsIRequest *aRequest,
|
||||
nsISupports *ctxt,
|
||||
nsIInputStream *inStr,
|
||||
uint64_t sourceOffset,
|
||||
uint32_t count)
|
||||
{
|
||||
uint32_t totalRead;
|
||||
return inStr->ReadSegments(NS_DiscardSegment, nullptr, count, &totalRead);
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
HSTSPrimingListener::StartHSTSPriming(nsIChannel* aRequestChannel,
|
||||
nsIHstsPrimingCallback* aCallback)
|
||||
{
|
||||
|
||||
nsCOMPtr<nsIURI> finalChannelURI;
|
||||
nsresult rv = NS_GetFinalChannelURI(aRequestChannel, getter_AddRefs(finalChannelURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = NS_GetSecureUpgradedURI(finalChannelURI, getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
// check the HSTS cache
|
||||
bool hsts;
|
||||
bool cached;
|
||||
nsCOMPtr<nsISiteSecurityService> sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, 0, &cached, &hsts);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (hsts) {
|
||||
// already saw this host and will upgrade if allowed by preferences
|
||||
return aCallback->OnHSTSPrimingSucceeded(true);
|
||||
}
|
||||
|
||||
if (cached) {
|
||||
// there is a non-expired entry in the cache that doesn't allow us to
|
||||
// upgrade, so go ahead and fail early.
|
||||
return aCallback->OnHSTSPrimingFailed(NS_ERROR_CONTENT_BLOCKED, true);
|
||||
}
|
||||
|
||||
// Either it wasn't cached or the cached result has expired. Build a
|
||||
// channel for the HEAD request.
|
||||
|
||||
nsCOMPtr<nsILoadInfo> originalLoadInfo = aRequestChannel->GetLoadInfo();
|
||||
MOZ_ASSERT(originalLoadInfo, "can not perform HSTS priming without a loadInfo");
|
||||
if (!originalLoadInfo) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = static_cast<mozilla::LoadInfo*>
|
||||
(originalLoadInfo.get())->CloneForNewRequest();
|
||||
|
||||
// the LoadInfo must have a security flag set in order to pass through priming
|
||||
// if none of these security flags are set, go ahead and fail now instead of
|
||||
// crashing in nsContentSecurityManager::ValidateSecurityFlags
|
||||
nsSecurityFlags securityMode = loadInfo->GetSecurityMode();
|
||||
if (securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS &&
|
||||
securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED &&
|
||||
securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS &&
|
||||
securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
|
||||
securityMode != nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
|
||||
return aCallback->OnHSTSPrimingFailed(NS_ERROR_CONTENT_BLOCKED, true);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||
rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsLoadFlags loadFlags;
|
||||
rv = aRequestChannel->GetLoadFlags(&loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
loadFlags &= HttpBaseChannel::INHIBIT_CACHING |
|
||||
HttpBaseChannel::INHIBIT_PERSISTENT_CACHING |
|
||||
HttpBaseChannel::LOAD_BYPASS_CACHE |
|
||||
HttpBaseChannel::LOAD_FROM_CACHE |
|
||||
HttpBaseChannel::VALIDATE_ALWAYS;
|
||||
// Priming requests should never be intercepted by service workers and
|
||||
// are always anonymous.
|
||||
loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER |
|
||||
nsIRequest::LOAD_ANONYMOUS;
|
||||
|
||||
// Create a new channel to send the priming request
|
||||
nsCOMPtr<nsIChannel> primingChannel;
|
||||
rv = NS_NewChannelInternal(getter_AddRefs(primingChannel),
|
||||
uri,
|
||||
loadInfo,
|
||||
loadGroup,
|
||||
nullptr, // aCallbacks are set later
|
||||
loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set method and headers
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(primingChannel);
|
||||
if (!httpChannel) {
|
||||
NS_ERROR("HSTSPrimingListener: Failed to QI to nsIHttpChannel!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Currently using HEAD per the draft, but under discussion to change to GET
|
||||
// with credentials so if the upgrade is approved the result is already cached.
|
||||
rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = httpChannel->
|
||||
SetRequestHeader(NS_LITERAL_CSTRING("Upgrade-Insecure-Requests"),
|
||||
NS_LITERAL_CSTRING("1"), false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// attempt to set the class of service flags on the new channel
|
||||
nsCOMPtr<nsIClassOfService> requestClass = do_QueryInterface(aRequestChannel);
|
||||
if (!requestClass) {
|
||||
NS_ERROR("HSTSPrimingListener: aRequestChannel is not an nsIClassOfService");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsCOMPtr<nsIClassOfService> primingClass = do_QueryInterface(httpChannel);
|
||||
if (!primingClass) {
|
||||
NS_ERROR("HSTSPrimingListener: aRequestChannel is not an nsIClassOfService");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
uint32_t classFlags = 0;
|
||||
rv = requestClass ->GetClassFlags(&classFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = primingClass->SetClassFlags(classFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set up listener which will start the original channel
|
||||
nsCOMPtr<nsIStreamListener> primingListener(new HSTSPrimingListener(aCallback));
|
||||
|
||||
// Start priming
|
||||
rv = primingChannel->AsyncOpen2(primingListener);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
108
netwerk/protocol/http/HSTSPrimerListener.h
Normal file
108
netwerk/protocol/http/HSTSPrimerListener.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 HSTSPrimingListener_h__
|
||||
#define HSTSPrimingListener_h__
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIThreadRetargetableStreamListener.h"
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
class nsINetworkInterceptController;
|
||||
class nsIHstsPrimingCallback;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class HttpChannelParent;
|
||||
class nsHttpChannel;
|
||||
|
||||
/*
|
||||
* How often do we get back an HSTS priming result which upgrades the connection to HTTPS?
|
||||
*/
|
||||
enum HSTSPrimingResult {
|
||||
// This site has been seen before and won't be upgraded
|
||||
eHSTS_PRIMING_CACHED_NO_UPGRADE = 0,
|
||||
// This site has been seen before and will be upgraded
|
||||
eHSTS_PRIMING_CACHED_DO_UPGRADE = 1,
|
||||
// This site has been seen before and will be blocked
|
||||
eHSTS_PRIMING_CACHED_BLOCK = 2,
|
||||
// The request was already upgraded, probably through
|
||||
// upgrade-insecure-requests
|
||||
eHSTS_PRIMING_ALREADY_UPGRADED = 3,
|
||||
// HSTS priming is successful and the connection will be upgraded to HTTPS
|
||||
eHSTS_PRIMING_SUCCEEDED = 4,
|
||||
// When priming succeeds, but preferences require preservation of the order
|
||||
// of mixed-content and hsts, and mixed-content blocks the load
|
||||
eHSTS_PRIMING_SUCCEEDED_BLOCK = 5,
|
||||
// When priming succeeds, but preferences require preservation of the order
|
||||
// of mixed-content and hsts, and mixed-content allows the load over http
|
||||
eHSTS_PRIMING_SUCCEEDED_HTTP = 6,
|
||||
// HSTS priming failed, and the load is blocked by mixed-content
|
||||
eHSTS_PRIMING_FAILED_BLOCK = 7,
|
||||
// HSTS priming failed, and the load is allowed by mixed-content
|
||||
eHSTS_PRIMING_FAILED_ACCEPT = 8
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Class used as streamlistener and notification callback when
|
||||
// doing the HEAD request for an HSTS Priming check. Needs to be an
|
||||
// nsIStreamListener in order to receive events from AsyncOpen2
|
||||
class HSTSPrimingListener final : public nsIStreamListener,
|
||||
public nsIInterfaceRequestor
|
||||
{
|
||||
public:
|
||||
explicit HSTSPrimingListener(nsIHstsPrimingCallback* aCallback)
|
||||
: mCallback(aCallback)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
private:
|
||||
~HSTSPrimingListener() {}
|
||||
|
||||
// Only nsHttpChannel can invoke HSTS priming
|
||||
friend class mozilla::net::nsHttpChannel;
|
||||
|
||||
/**
|
||||
* Start the HSTS priming request. This will send an anonymous HEAD request to
|
||||
* the URI aRequestChannel is attempting to load. On success, the new HSTS
|
||||
* priming channel is allocated in aHSTSPrimingChannel.
|
||||
*
|
||||
* @param aRequestChannel the reference channel used to initialze the HSTS
|
||||
* priming channel
|
||||
* @param aCallback the callback stored to handle the results of HSTS priming.
|
||||
* @param aHSTSPrimingChannel if the new HSTS priming channel is allocated
|
||||
* successfully, it will be placed here.
|
||||
*/
|
||||
static nsresult StartHSTSPriming(nsIChannel* aRequestChannel,
|
||||
nsIHstsPrimingCallback* aCallback);
|
||||
|
||||
/**
|
||||
* Given a request, return NS_OK if it has resulted in a cached HSTS update.
|
||||
* We don't need to check for the header as that has already been done for us.
|
||||
*/
|
||||
nsresult CheckHSTSPrimingRequestStatus(nsIRequest* aRequest);
|
||||
|
||||
/**
|
||||
* the nsIHttpChannel to notify with the result of HSTS priming.
|
||||
*/
|
||||
nsCOMPtr<nsIHstsPrimingCallback> mCallback;
|
||||
};
|
||||
|
||||
|
||||
}} // mozilla::net
|
||||
|
||||
#endif // HSTSPrimingListener_h__
|
||||
1487
netwerk/protocol/http/Http2Compression.cpp
Normal file
1487
netwerk/protocol/http/Http2Compression.cpp
Normal file
File diff suppressed because it is too large
Load diff
202
netwerk/protocol/http/Http2Compression.h
Normal file
202
netwerk/protocol/http/Http2Compression.h
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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_Http2Compression_Internal_h
|
||||
#define mozilla_net_Http2Compression_Internal_h
|
||||
|
||||
// HPACK - RFC 7541
|
||||
// https://www.rfc-editor.org/rfc/rfc7541.txt
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsDeque.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
struct HuffmanIncomingTable;
|
||||
|
||||
void Http2CompressionCleanup();
|
||||
|
||||
class nvPair
|
||||
{
|
||||
public:
|
||||
nvPair(const nsACString &name, const nsACString &value)
|
||||
: mName(name)
|
||||
, mValue(value)
|
||||
{ }
|
||||
|
||||
uint32_t Size() const { return mName.Length() + mValue.Length() + 32; }
|
||||
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
nsCString mName;
|
||||
nsCString mValue;
|
||||
};
|
||||
|
||||
class nvFIFO
|
||||
{
|
||||
public:
|
||||
nvFIFO();
|
||||
~nvFIFO();
|
||||
void AddElement(const nsCString &name, const nsCString &value);
|
||||
void AddElement(const nsCString &name);
|
||||
void RemoveElement();
|
||||
uint32_t ByteCount() const;
|
||||
uint32_t Length() const;
|
||||
uint32_t VariableLength() const;
|
||||
size_t StaticLength() const;
|
||||
void Clear();
|
||||
const nvPair *operator[] (size_t index) const;
|
||||
|
||||
private:
|
||||
uint32_t mByteCount;
|
||||
nsDeque mTable;
|
||||
};
|
||||
|
||||
class HpackDynamicTableReporter;
|
||||
|
||||
class Http2BaseCompressor
|
||||
{
|
||||
public:
|
||||
Http2BaseCompressor();
|
||||
virtual ~Http2BaseCompressor();
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
nsresult SetInitialMaxBufferSize(uint32_t maxBufferSize);
|
||||
|
||||
protected:
|
||||
const static uint32_t kDefaultMaxBuffer = 4096;
|
||||
|
||||
virtual void ClearHeaderTable();
|
||||
virtual void MakeRoom(uint32_t amount, const char *direction);
|
||||
virtual void DumpState();
|
||||
virtual void SetMaxBufferSizeInternal(uint32_t maxBufferSize);
|
||||
|
||||
nsACString *mOutput;
|
||||
nvFIFO mHeaderTable;
|
||||
|
||||
uint32_t mMaxBuffer;
|
||||
uint32_t mMaxBufferSetting;
|
||||
bool mSetInitialMaxBufferSizeAllowed;
|
||||
|
||||
uint32_t mPeakSize;
|
||||
uint32_t mPeakCount;
|
||||
MOZ_INIT_OUTSIDE_CTOR
|
||||
Telemetry::ID mPeakSizeID;
|
||||
MOZ_INIT_OUTSIDE_CTOR
|
||||
Telemetry::ID mPeakCountID;
|
||||
|
||||
private:
|
||||
RefPtr<HpackDynamicTableReporter> mDynamicReporter;
|
||||
};
|
||||
|
||||
class Http2Compressor;
|
||||
|
||||
class Http2Decompressor final : public Http2BaseCompressor
|
||||
{
|
||||
public:
|
||||
Http2Decompressor()
|
||||
{
|
||||
mPeakSizeID = Telemetry::HPACK_PEAK_SIZE_DECOMPRESSOR;
|
||||
mPeakCountID = Telemetry::HPACK_PEAK_COUNT_DECOMPRESSOR;
|
||||
};
|
||||
virtual ~Http2Decompressor() { } ;
|
||||
|
||||
// NS_OK: Produces the working set of HTTP/1 formatted headers
|
||||
nsresult DecodeHeaderBlock(const uint8_t *data, uint32_t datalen,
|
||||
nsACString &output, bool isPush);
|
||||
|
||||
void GetStatus(nsACString &hdr) { hdr = mHeaderStatus; }
|
||||
void GetHost(nsACString &hdr) { hdr = mHeaderHost; }
|
||||
void GetScheme(nsACString &hdr) { hdr = mHeaderScheme; }
|
||||
void GetPath(nsACString &hdr) { hdr = mHeaderPath; }
|
||||
void GetMethod(nsACString &hdr) { hdr = mHeaderMethod; }
|
||||
|
||||
private:
|
||||
nsresult DoIndexed();
|
||||
nsresult DoLiteralWithoutIndex();
|
||||
nsresult DoLiteralWithIncremental();
|
||||
nsresult DoLiteralInternal(nsACString &, nsACString &, uint32_t);
|
||||
nsresult DoLiteralNeverIndexed();
|
||||
nsresult DoContextUpdate();
|
||||
|
||||
nsresult DecodeInteger(uint32_t prefixLen, uint32_t &result);
|
||||
nsresult OutputHeader(uint32_t index);
|
||||
nsresult OutputHeader(const nsACString &name, const nsACString &value);
|
||||
|
||||
nsresult CopyHeaderString(uint32_t index, nsACString &name);
|
||||
nsresult CopyStringFromInput(uint32_t index, nsACString &val);
|
||||
uint8_t ExtractByte(uint8_t bitsLeft, uint32_t &bytesConsumed);
|
||||
nsresult CopyHuffmanStringFromInput(uint32_t index, nsACString &val);
|
||||
nsresult DecodeHuffmanCharacter(const HuffmanIncomingTable *table, uint8_t &c,
|
||||
uint32_t &bytesConsumed, uint8_t &bitsLeft);
|
||||
nsresult DecodeFinalHuffmanCharacter(const HuffmanIncomingTable *table,
|
||||
uint8_t &c, uint8_t &bitsLeft);
|
||||
|
||||
nsCString mHeaderStatus;
|
||||
nsCString mHeaderHost;
|
||||
nsCString mHeaderScheme;
|
||||
nsCString mHeaderPath;
|
||||
nsCString mHeaderMethod;
|
||||
|
||||
// state variables when DecodeBlock() is on the stack
|
||||
uint32_t mOffset;
|
||||
const uint8_t *mData;
|
||||
uint32_t mDataLen;
|
||||
bool mSeenNonColonHeader;
|
||||
bool mIsPush;
|
||||
};
|
||||
|
||||
|
||||
class Http2Compressor final : public Http2BaseCompressor
|
||||
{
|
||||
public:
|
||||
Http2Compressor() : mParsedContentLength(-1),
|
||||
mBufferSizeChangeWaiting(false),
|
||||
mLowestBufferSizeWaiting(0)
|
||||
{
|
||||
mPeakSizeID = Telemetry::HPACK_PEAK_SIZE_COMPRESSOR;
|
||||
mPeakCountID = Telemetry::HPACK_PEAK_COUNT_COMPRESSOR;
|
||||
};
|
||||
virtual ~Http2Compressor() { }
|
||||
|
||||
// HTTP/1 formatted header block as input - HTTP/2 formatted
|
||||
// header block as output
|
||||
nsresult EncodeHeaderBlock(const nsCString &nvInput,
|
||||
const nsACString &method, const nsACString &path,
|
||||
const nsACString &host, const nsACString &scheme,
|
||||
bool connectForm, nsACString &output);
|
||||
|
||||
int64_t GetParsedContentLength() { return mParsedContentLength; } // -1 on not found
|
||||
|
||||
void SetMaxBufferSize(uint32_t maxBufferSize);
|
||||
|
||||
private:
|
||||
enum outputCode {
|
||||
kNeverIndexedLiteral,
|
||||
kPlainLiteral,
|
||||
kIndexedLiteral,
|
||||
kIndex
|
||||
};
|
||||
|
||||
void DoOutput(Http2Compressor::outputCode code,
|
||||
const class nvPair *pair, uint32_t index);
|
||||
void EncodeInteger(uint32_t prefixLen, uint32_t val);
|
||||
void ProcessHeader(const nvPair inputPair, bool noLocalIndex,
|
||||
bool neverIndex);
|
||||
void HuffmanAppend(const nsCString &value);
|
||||
void EncodeTableSizeChange(uint32_t newMaxSize);
|
||||
|
||||
int64_t mParsedContentLength;
|
||||
bool mBufferSizeChangeWaiting;
|
||||
uint32_t mLowestBufferSizeWaiting;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_Http2Compression_Internal_h
|
||||
4054
netwerk/protocol/http/Http2HuffmanIncoming.h
Normal file
4054
netwerk/protocol/http/Http2HuffmanIncoming.h
Normal file
File diff suppressed because it is too large
Load diff
278
netwerk/protocol/http/Http2HuffmanOutgoing.h
Normal file
278
netwerk/protocol/http/Http2HuffmanOutgoing.h
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* THIS FILE IS AUTO-GENERATED. DO NOT EDIT!
|
||||
*/
|
||||
#ifndef mozilla__net__Http2HuffmanOutgoing_h
|
||||
#define mozilla__net__Http2HuffmanOutgoing_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
struct HuffmanOutgoingEntry {
|
||||
uint32_t mValue;
|
||||
uint8_t mLength;
|
||||
};
|
||||
|
||||
static HuffmanOutgoingEntry HuffmanOutgoing[] = {
|
||||
{ 0x00001ff8, 13 },
|
||||
{ 0x007fffd8, 23 },
|
||||
{ 0x0fffffe2, 28 },
|
||||
{ 0x0fffffe3, 28 },
|
||||
{ 0x0fffffe4, 28 },
|
||||
{ 0x0fffffe5, 28 },
|
||||
{ 0x0fffffe6, 28 },
|
||||
{ 0x0fffffe7, 28 },
|
||||
{ 0x0fffffe8, 28 },
|
||||
{ 0x00ffffea, 24 },
|
||||
{ 0x3ffffffc, 30 },
|
||||
{ 0x0fffffe9, 28 },
|
||||
{ 0x0fffffea, 28 },
|
||||
{ 0x3ffffffd, 30 },
|
||||
{ 0x0fffffeb, 28 },
|
||||
{ 0x0fffffec, 28 },
|
||||
{ 0x0fffffed, 28 },
|
||||
{ 0x0fffffee, 28 },
|
||||
{ 0x0fffffef, 28 },
|
||||
{ 0x0ffffff0, 28 },
|
||||
{ 0x0ffffff1, 28 },
|
||||
{ 0x0ffffff2, 28 },
|
||||
{ 0x3ffffffe, 30 },
|
||||
{ 0x0ffffff3, 28 },
|
||||
{ 0x0ffffff4, 28 },
|
||||
{ 0x0ffffff5, 28 },
|
||||
{ 0x0ffffff6, 28 },
|
||||
{ 0x0ffffff7, 28 },
|
||||
{ 0x0ffffff8, 28 },
|
||||
{ 0x0ffffff9, 28 },
|
||||
{ 0x0ffffffa, 28 },
|
||||
{ 0x0ffffffb, 28 },
|
||||
{ 0x00000014, 6 },
|
||||
{ 0x000003f8, 10 },
|
||||
{ 0x000003f9, 10 },
|
||||
{ 0x00000ffa, 12 },
|
||||
{ 0x00001ff9, 13 },
|
||||
{ 0x00000015, 6 },
|
||||
{ 0x000000f8, 8 },
|
||||
{ 0x000007fa, 11 },
|
||||
{ 0x000003fa, 10 },
|
||||
{ 0x000003fb, 10 },
|
||||
{ 0x000000f9, 8 },
|
||||
{ 0x000007fb, 11 },
|
||||
{ 0x000000fa, 8 },
|
||||
{ 0x00000016, 6 },
|
||||
{ 0x00000017, 6 },
|
||||
{ 0x00000018, 6 },
|
||||
{ 0x00000000, 5 },
|
||||
{ 0x00000001, 5 },
|
||||
{ 0x00000002, 5 },
|
||||
{ 0x00000019, 6 },
|
||||
{ 0x0000001a, 6 },
|
||||
{ 0x0000001b, 6 },
|
||||
{ 0x0000001c, 6 },
|
||||
{ 0x0000001d, 6 },
|
||||
{ 0x0000001e, 6 },
|
||||
{ 0x0000001f, 6 },
|
||||
{ 0x0000005c, 7 },
|
||||
{ 0x000000fb, 8 },
|
||||
{ 0x00007ffc, 15 },
|
||||
{ 0x00000020, 6 },
|
||||
{ 0x00000ffb, 12 },
|
||||
{ 0x000003fc, 10 },
|
||||
{ 0x00001ffa, 13 },
|
||||
{ 0x00000021, 6 },
|
||||
{ 0x0000005d, 7 },
|
||||
{ 0x0000005e, 7 },
|
||||
{ 0x0000005f, 7 },
|
||||
{ 0x00000060, 7 },
|
||||
{ 0x00000061, 7 },
|
||||
{ 0x00000062, 7 },
|
||||
{ 0x00000063, 7 },
|
||||
{ 0x00000064, 7 },
|
||||
{ 0x00000065, 7 },
|
||||
{ 0x00000066, 7 },
|
||||
{ 0x00000067, 7 },
|
||||
{ 0x00000068, 7 },
|
||||
{ 0x00000069, 7 },
|
||||
{ 0x0000006a, 7 },
|
||||
{ 0x0000006b, 7 },
|
||||
{ 0x0000006c, 7 },
|
||||
{ 0x0000006d, 7 },
|
||||
{ 0x0000006e, 7 },
|
||||
{ 0x0000006f, 7 },
|
||||
{ 0x00000070, 7 },
|
||||
{ 0x00000071, 7 },
|
||||
{ 0x00000072, 7 },
|
||||
{ 0x000000fc, 8 },
|
||||
{ 0x00000073, 7 },
|
||||
{ 0x000000fd, 8 },
|
||||
{ 0x00001ffb, 13 },
|
||||
{ 0x0007fff0, 19 },
|
||||
{ 0x00001ffc, 13 },
|
||||
{ 0x00003ffc, 14 },
|
||||
{ 0x00000022, 6 },
|
||||
{ 0x00007ffd, 15 },
|
||||
{ 0x00000003, 5 },
|
||||
{ 0x00000023, 6 },
|
||||
{ 0x00000004, 5 },
|
||||
{ 0x00000024, 6 },
|
||||
{ 0x00000005, 5 },
|
||||
{ 0x00000025, 6 },
|
||||
{ 0x00000026, 6 },
|
||||
{ 0x00000027, 6 },
|
||||
{ 0x00000006, 5 },
|
||||
{ 0x00000074, 7 },
|
||||
{ 0x00000075, 7 },
|
||||
{ 0x00000028, 6 },
|
||||
{ 0x00000029, 6 },
|
||||
{ 0x0000002a, 6 },
|
||||
{ 0x00000007, 5 },
|
||||
{ 0x0000002b, 6 },
|
||||
{ 0x00000076, 7 },
|
||||
{ 0x0000002c, 6 },
|
||||
{ 0x00000008, 5 },
|
||||
{ 0x00000009, 5 },
|
||||
{ 0x0000002d, 6 },
|
||||
{ 0x00000077, 7 },
|
||||
{ 0x00000078, 7 },
|
||||
{ 0x00000079, 7 },
|
||||
{ 0x0000007a, 7 },
|
||||
{ 0x0000007b, 7 },
|
||||
{ 0x00007ffe, 15 },
|
||||
{ 0x000007fc, 11 },
|
||||
{ 0x00003ffd, 14 },
|
||||
{ 0x00001ffd, 13 },
|
||||
{ 0x0ffffffc, 28 },
|
||||
{ 0x000fffe6, 20 },
|
||||
{ 0x003fffd2, 22 },
|
||||
{ 0x000fffe7, 20 },
|
||||
{ 0x000fffe8, 20 },
|
||||
{ 0x003fffd3, 22 },
|
||||
{ 0x003fffd4, 22 },
|
||||
{ 0x003fffd5, 22 },
|
||||
{ 0x007fffd9, 23 },
|
||||
{ 0x003fffd6, 22 },
|
||||
{ 0x007fffda, 23 },
|
||||
{ 0x007fffdb, 23 },
|
||||
{ 0x007fffdc, 23 },
|
||||
{ 0x007fffdd, 23 },
|
||||
{ 0x007fffde, 23 },
|
||||
{ 0x00ffffeb, 24 },
|
||||
{ 0x007fffdf, 23 },
|
||||
{ 0x00ffffec, 24 },
|
||||
{ 0x00ffffed, 24 },
|
||||
{ 0x003fffd7, 22 },
|
||||
{ 0x007fffe0, 23 },
|
||||
{ 0x00ffffee, 24 },
|
||||
{ 0x007fffe1, 23 },
|
||||
{ 0x007fffe2, 23 },
|
||||
{ 0x007fffe3, 23 },
|
||||
{ 0x007fffe4, 23 },
|
||||
{ 0x001fffdc, 21 },
|
||||
{ 0x003fffd8, 22 },
|
||||
{ 0x007fffe5, 23 },
|
||||
{ 0x003fffd9, 22 },
|
||||
{ 0x007fffe6, 23 },
|
||||
{ 0x007fffe7, 23 },
|
||||
{ 0x00ffffef, 24 },
|
||||
{ 0x003fffda, 22 },
|
||||
{ 0x001fffdd, 21 },
|
||||
{ 0x000fffe9, 20 },
|
||||
{ 0x003fffdb, 22 },
|
||||
{ 0x003fffdc, 22 },
|
||||
{ 0x007fffe8, 23 },
|
||||
{ 0x007fffe9, 23 },
|
||||
{ 0x001fffde, 21 },
|
||||
{ 0x007fffea, 23 },
|
||||
{ 0x003fffdd, 22 },
|
||||
{ 0x003fffde, 22 },
|
||||
{ 0x00fffff0, 24 },
|
||||
{ 0x001fffdf, 21 },
|
||||
{ 0x003fffdf, 22 },
|
||||
{ 0x007fffeb, 23 },
|
||||
{ 0x007fffec, 23 },
|
||||
{ 0x001fffe0, 21 },
|
||||
{ 0x001fffe1, 21 },
|
||||
{ 0x003fffe0, 22 },
|
||||
{ 0x001fffe2, 21 },
|
||||
{ 0x007fffed, 23 },
|
||||
{ 0x003fffe1, 22 },
|
||||
{ 0x007fffee, 23 },
|
||||
{ 0x007fffef, 23 },
|
||||
{ 0x000fffea, 20 },
|
||||
{ 0x003fffe2, 22 },
|
||||
{ 0x003fffe3, 22 },
|
||||
{ 0x003fffe4, 22 },
|
||||
{ 0x007ffff0, 23 },
|
||||
{ 0x003fffe5, 22 },
|
||||
{ 0x003fffe6, 22 },
|
||||
{ 0x007ffff1, 23 },
|
||||
{ 0x03ffffe0, 26 },
|
||||
{ 0x03ffffe1, 26 },
|
||||
{ 0x000fffeb, 20 },
|
||||
{ 0x0007fff1, 19 },
|
||||
{ 0x003fffe7, 22 },
|
||||
{ 0x007ffff2, 23 },
|
||||
{ 0x003fffe8, 22 },
|
||||
{ 0x01ffffec, 25 },
|
||||
{ 0x03ffffe2, 26 },
|
||||
{ 0x03ffffe3, 26 },
|
||||
{ 0x03ffffe4, 26 },
|
||||
{ 0x07ffffde, 27 },
|
||||
{ 0x07ffffdf, 27 },
|
||||
{ 0x03ffffe5, 26 },
|
||||
{ 0x00fffff1, 24 },
|
||||
{ 0x01ffffed, 25 },
|
||||
{ 0x0007fff2, 19 },
|
||||
{ 0x001fffe3, 21 },
|
||||
{ 0x03ffffe6, 26 },
|
||||
{ 0x07ffffe0, 27 },
|
||||
{ 0x07ffffe1, 27 },
|
||||
{ 0x03ffffe7, 26 },
|
||||
{ 0x07ffffe2, 27 },
|
||||
{ 0x00fffff2, 24 },
|
||||
{ 0x001fffe4, 21 },
|
||||
{ 0x001fffe5, 21 },
|
||||
{ 0x03ffffe8, 26 },
|
||||
{ 0x03ffffe9, 26 },
|
||||
{ 0x0ffffffd, 28 },
|
||||
{ 0x07ffffe3, 27 },
|
||||
{ 0x07ffffe4, 27 },
|
||||
{ 0x07ffffe5, 27 },
|
||||
{ 0x000fffec, 20 },
|
||||
{ 0x00fffff3, 24 },
|
||||
{ 0x000fffed, 20 },
|
||||
{ 0x001fffe6, 21 },
|
||||
{ 0x003fffe9, 22 },
|
||||
{ 0x001fffe7, 21 },
|
||||
{ 0x001fffe8, 21 },
|
||||
{ 0x007ffff3, 23 },
|
||||
{ 0x003fffea, 22 },
|
||||
{ 0x003fffeb, 22 },
|
||||
{ 0x01ffffee, 25 },
|
||||
{ 0x01ffffef, 25 },
|
||||
{ 0x00fffff4, 24 },
|
||||
{ 0x00fffff5, 24 },
|
||||
{ 0x03ffffea, 26 },
|
||||
{ 0x007ffff4, 23 },
|
||||
{ 0x03ffffeb, 26 },
|
||||
{ 0x07ffffe6, 27 },
|
||||
{ 0x03ffffec, 26 },
|
||||
{ 0x03ffffed, 26 },
|
||||
{ 0x07ffffe7, 27 },
|
||||
{ 0x07ffffe8, 27 },
|
||||
{ 0x07ffffe9, 27 },
|
||||
{ 0x07ffffea, 27 },
|
||||
{ 0x07ffffeb, 27 },
|
||||
{ 0x0ffffffe, 28 },
|
||||
{ 0x07ffffec, 27 },
|
||||
{ 0x07ffffed, 27 },
|
||||
{ 0x07ffffee, 27 },
|
||||
{ 0x07ffffef, 27 },
|
||||
{ 0x07fffff0, 27 },
|
||||
{ 0x03ffffee, 26 },
|
||||
{ 0x3fffffff, 30 }
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla__net__Http2HuffmanOutgoing_h
|
||||
510
netwerk/protocol/http/Http2Push.cpp
Normal file
510
netwerk/protocol/http/Http2Push.cpp
Normal file
|
|
@ -0,0 +1,510 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/. */
|
||||
|
||||
// HttpLog.h should generally be included first
|
||||
#include "HttpLog.h"
|
||||
|
||||
// Log on level :5, instead of default :4.
|
||||
#undef LOG
|
||||
#define LOG(args) LOG5(args)
|
||||
#undef LOG_ENABLED
|
||||
#define LOG_ENABLED() LOG5_ENABLED()
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Http2Push.h"
|
||||
#include "nsHttpChannel.h"
|
||||
#include "nsIHttpPushListener.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class CallChannelOnPush final : public Runnable {
|
||||
public:
|
||||
CallChannelOnPush(nsIHttpChannelInternal *associatedChannel,
|
||||
const nsACString &pushedURI,
|
||||
Http2PushedStream *pushStream)
|
||||
: mAssociatedChannel(associatedChannel)
|
||||
, mPushedURI(pushedURI)
|
||||
, mPushedStream(pushStream)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
RefPtr<nsHttpChannel> channel;
|
||||
CallQueryInterface(mAssociatedChannel, channel.StartAssignment());
|
||||
MOZ_ASSERT(channel);
|
||||
if (channel && NS_SUCCEEDED(channel->OnPush(mPushedURI, mPushedStream))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
LOG3(("Http2PushedStream Orphan %p failed OnPush\n", this));
|
||||
mPushedStream->OnPushFailed();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIHttpChannelInternal> mAssociatedChannel;
|
||||
const nsCString mPushedURI;
|
||||
Http2PushedStream *mPushedStream;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Http2PushedStream
|
||||
//////////////////////////////////////////
|
||||
|
||||
Http2PushedStream::Http2PushedStream(Http2PushTransactionBuffer *aTransaction,
|
||||
Http2Session *aSession,
|
||||
Http2Stream *aAssociatedStream,
|
||||
uint32_t aID)
|
||||
:Http2Stream(aTransaction, aSession, 0)
|
||||
, mConsumerStream(nullptr)
|
||||
, mAssociatedTransaction(aAssociatedStream->Transaction())
|
||||
, mBufferedPush(aTransaction)
|
||||
, mStatus(NS_OK)
|
||||
, mPushCompleted(false)
|
||||
, mDeferCleanupOnSuccess(true)
|
||||
, mDeferCleanupOnPush(false)
|
||||
, mOnPushFailed(false)
|
||||
{
|
||||
LOG3(("Http2PushedStream ctor this=%p 0x%X\n", this, aID));
|
||||
mStreamID = aID;
|
||||
MOZ_ASSERT(!(aID & 1)); // must be even to be a pushed stream
|
||||
mBufferedPush->SetPushStream(this);
|
||||
mRequestContext = aAssociatedStream->RequestContext();
|
||||
mLastRead = TimeStamp::Now();
|
||||
SetPriority(aAssociatedStream->Priority() + 1);
|
||||
}
|
||||
|
||||
bool
|
||||
Http2PushedStream::GetPushComplete()
|
||||
{
|
||||
return mPushCompleted;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Http2PushedStream::WriteSegments(nsAHttpSegmentWriter *writer,
|
||||
uint32_t count, uint32_t *countWritten)
|
||||
{
|
||||
nsresult rv = Http2Stream::WriteSegments(writer, count, countWritten);
|
||||
if (NS_SUCCEEDED(rv) && *countWritten) {
|
||||
mLastRead = TimeStamp::Now();
|
||||
}
|
||||
|
||||
if (rv == NS_BASE_STREAM_CLOSED) {
|
||||
mPushCompleted = true;
|
||||
rv = NS_OK; // this is what a normal HTTP transaction would do
|
||||
}
|
||||
if (rv != NS_BASE_STREAM_WOULD_BLOCK && NS_FAILED(rv))
|
||||
mStatus = rv;
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
Http2PushedStream::DeferCleanup(nsresult status)
|
||||
{
|
||||
LOG3(("Http2PushedStream::DeferCleanup Query %p %x\n", this, status));
|
||||
|
||||
if (NS_SUCCEEDED(status) && mDeferCleanupOnSuccess) {
|
||||
LOG3(("Http2PushedStream::DeferCleanup %p %x defer on success\n", this, status));
|
||||
return true;
|
||||
}
|
||||
if (mDeferCleanupOnPush) {
|
||||
LOG3(("Http2PushedStream::DeferCleanup %p %x defer onPush ref\n", this, status));
|
||||
return true;
|
||||
}
|
||||
if (mConsumerStream) {
|
||||
LOG3(("Http2PushedStream::DeferCleanup %p %x defer active consumer\n", this, status));
|
||||
return true;
|
||||
}
|
||||
LOG3(("Http2PushedStream::DeferCleanup Query %p %x not deferred\n", this, status));
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if channel implements nsIHttpPushListener
|
||||
bool
|
||||
Http2PushedStream::TryOnPush()
|
||||
{
|
||||
nsHttpTransaction *trans = mAssociatedTransaction->QueryHttpTransaction();
|
||||
if (!trans) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIHttpChannelInternal> associatedChannel = do_QueryInterface(trans->HttpChannel());
|
||||
if (!associatedChannel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(trans->Caps() & NS_HTTP_ONPUSH_LISTENER)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mDeferCleanupOnPush = true;
|
||||
nsCString uri = Origin() + Path();
|
||||
NS_DispatchToMainThread(new CallChannelOnPush(associatedChannel, uri, this));
|
||||
return true;
|
||||
}
|
||||
|
||||
// side effect free static method to determine if Http2Stream implements nsIHttpPushListener
|
||||
bool
|
||||
Http2PushedStream::TestOnPush(Http2Stream *stream)
|
||||
{
|
||||
if (!stream) {
|
||||
return false;
|
||||
}
|
||||
nsAHttpTransaction *abstractTransaction = stream->Transaction();
|
||||
if (!abstractTransaction) {
|
||||
return false;
|
||||
}
|
||||
nsHttpTransaction *trans = abstractTransaction->QueryHttpTransaction();
|
||||
if (!trans) {
|
||||
return false;
|
||||
}
|
||||
nsCOMPtr<nsIHttpChannelInternal> associatedChannel = do_QueryInterface(trans->HttpChannel());
|
||||
if (!associatedChannel) {
|
||||
return false;
|
||||
}
|
||||
return (trans->Caps() & NS_HTTP_ONPUSH_LISTENER);
|
||||
}
|
||||
|
||||
nsresult
|
||||
Http2PushedStream::ReadSegments(nsAHttpSegmentReader *reader,
|
||||
uint32_t, uint32_t *count)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
*count = 0;
|
||||
|
||||
switch (mUpstreamState) {
|
||||
case GENERATING_HEADERS:
|
||||
// The request headers for this has been processed, so we need to verify
|
||||
// that :authority, :scheme, and :path MUST be present. :method MUST NOT be
|
||||
// present
|
||||
CreatePushHashKey(mHeaderScheme, mHeaderHost,
|
||||
mSession->Serial(), mHeaderPath,
|
||||
mOrigin, mHashKey);
|
||||
|
||||
LOG3(("Http2PushStream 0x%X hash key %s\n", mStreamID, mHashKey.get()));
|
||||
|
||||
// the write side of a pushed transaction just involves manipulating a little state
|
||||
SetSentFin(true);
|
||||
Http2Stream::mRequestHeadersDone = 1;
|
||||
Http2Stream::mOpenGenerated = 1;
|
||||
Http2Stream::ChangeState(UPSTREAM_COMPLETE);
|
||||
break;
|
||||
|
||||
case UPSTREAM_COMPLETE:
|
||||
// Let's just clear the stream's transmit buffer by pushing it into
|
||||
// the session. This is probably a window adjustment.
|
||||
LOG3(("Http2Push::ReadSegments 0x%X \n", mStreamID));
|
||||
mSegmentReader = reader;
|
||||
rv = TransmitFrame(nullptr, nullptr, true);
|
||||
mSegmentReader = nullptr;
|
||||
break;
|
||||
|
||||
case GENERATING_BODY:
|
||||
case SENDING_BODY:
|
||||
case SENDING_FIN_STREAM:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
Http2PushedStream::AdjustInitialWindow()
|
||||
{
|
||||
LOG3(("Http2PushStream %p 0x%X AdjustInitialWindow", this, mStreamID));
|
||||
if (mConsumerStream) {
|
||||
LOG3(("Http2PushStream::AdjustInitialWindow %p 0x%X "
|
||||
"calling super consumer %p 0x%X\n", this,
|
||||
mStreamID, mConsumerStream, mConsumerStream->StreamID()));
|
||||
Http2Stream::AdjustInitialWindow();
|
||||
// Http2PushedStream::ReadSegments is needed to call TransmitFrame()
|
||||
// and actually get this information into the session bytestream
|
||||
mSession->TransactionHasDataToWrite(this);
|
||||
}
|
||||
// Otherwise, when we get hooked up, the initial window will get bumped
|
||||
// anyway, so we're good to go.
|
||||
}
|
||||
|
||||
void
|
||||
Http2PushedStream::SetConsumerStream(Http2Stream *consumer)
|
||||
{
|
||||
mConsumerStream = consumer;
|
||||
mDeferCleanupOnPush = false;
|
||||
}
|
||||
|
||||
bool
|
||||
Http2PushedStream::GetHashKey(nsCString &key)
|
||||
{
|
||||
if (mHashKey.IsEmpty())
|
||||
return false;
|
||||
|
||||
key = mHashKey;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Http2PushedStream::ConnectPushedStream(Http2Stream *stream)
|
||||
{
|
||||
mSession->ConnectPushedStream(stream);
|
||||
}
|
||||
|
||||
bool
|
||||
Http2PushedStream::IsOrphaned(TimeStamp now)
|
||||
{
|
||||
MOZ_ASSERT(!now.IsNull());
|
||||
|
||||
// if session is not transmitting, and is also not connected to a consumer
|
||||
// stream, and its been like that for too long then it is oprhaned
|
||||
|
||||
if (mConsumerStream || mDeferCleanupOnPush) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mOnPushFailed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rv = ((now - mLastRead).ToSeconds() > 30.0);
|
||||
if (rv) {
|
||||
LOG3(("Http2PushedStream:IsOrphaned 0x%X IsOrphaned %3.2f\n",
|
||||
mStreamID, (now - mLastRead).ToSeconds()));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Http2PushedStream::GetBufferedData(char *buf,
|
||||
uint32_t count, uint32_t *countWritten)
|
||||
{
|
||||
if (NS_FAILED(mStatus))
|
||||
return mStatus;
|
||||
|
||||
nsresult rv = mBufferedPush->GetBufferedData(buf, count, countWritten);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (!*countWritten)
|
||||
rv = GetPushComplete() ? NS_BASE_STREAM_CLOSED : NS_BASE_STREAM_WOULD_BLOCK;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Http2PushTransactionBuffer
|
||||
// This is the nsAHttpTransction owned by the stream when the pushed
|
||||
// stream has not yet been matched with a pull request
|
||||
//////////////////////////////////////////
|
||||
|
||||
NS_IMPL_ISUPPORTS0(Http2PushTransactionBuffer)
|
||||
|
||||
Http2PushTransactionBuffer::Http2PushTransactionBuffer()
|
||||
: mStatus(NS_OK)
|
||||
, mRequestHead(nullptr)
|
||||
, mPushStream(nullptr)
|
||||
, mIsDone(false)
|
||||
, mBufferedHTTP1Size(kDefaultBufferSize)
|
||||
, mBufferedHTTP1Used(0)
|
||||
, mBufferedHTTP1Consumed(0)
|
||||
{
|
||||
mBufferedHTTP1 = MakeUnique<char[]>(mBufferedHTTP1Size);
|
||||
}
|
||||
|
||||
Http2PushTransactionBuffer::~Http2PushTransactionBuffer()
|
||||
{
|
||||
delete mRequestHead;
|
||||
}
|
||||
|
||||
void
|
||||
Http2PushTransactionBuffer::SetConnection(nsAHttpConnection *conn)
|
||||
{
|
||||
}
|
||||
|
||||
nsAHttpConnection *
|
||||
Http2PushTransactionBuffer::Connection()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Http2PushTransactionBuffer::GetSecurityCallbacks(nsIInterfaceRequestor **outCB)
|
||||
{
|
||||
*outCB = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Http2PushTransactionBuffer::OnTransportStatus(nsITransport* transport,
|
||||
nsresult status, int64_t progress)
|
||||
{
|
||||
}
|
||||
|
||||
nsHttpConnectionInfo *
|
||||
Http2PushTransactionBuffer::ConnectionInfo()
|
||||
{
|
||||
if (!mPushStream) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!mPushStream->Transaction()) {
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(mPushStream->Transaction() != this);
|
||||
return mPushStream->Transaction()->ConnectionInfo();
|
||||
}
|
||||
|
||||
bool
|
||||
Http2PushTransactionBuffer::IsDone()
|
||||
{
|
||||
return mIsDone;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Http2PushTransactionBuffer::Status()
|
||||
{
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Http2PushTransactionBuffer::Caps()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
Http2PushTransactionBuffer::SetDNSWasRefreshed()
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t
|
||||
Http2PushTransactionBuffer::Available()
|
||||
{
|
||||
return mBufferedHTTP1Used - mBufferedHTTP1Consumed;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Http2PushTransactionBuffer::ReadSegments(nsAHttpSegmentReader *reader,
|
||||
uint32_t count, uint32_t *countRead)
|
||||
{
|
||||
*countRead = 0;
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Http2PushTransactionBuffer::WriteSegments(nsAHttpSegmentWriter *writer,
|
||||
uint32_t count, uint32_t *countWritten)
|
||||
{
|
||||
if ((mBufferedHTTP1Size - mBufferedHTTP1Used) < 20480) {
|
||||
EnsureBuffer(mBufferedHTTP1,mBufferedHTTP1Size + kDefaultBufferSize,
|
||||
mBufferedHTTP1Used, mBufferedHTTP1Size);
|
||||
}
|
||||
|
||||
count = std::min(count, mBufferedHTTP1Size - mBufferedHTTP1Used);
|
||||
nsresult rv = writer->OnWriteSegment(&mBufferedHTTP1[mBufferedHTTP1Used],
|
||||
count, countWritten);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mBufferedHTTP1Used += *countWritten;
|
||||
}
|
||||
else if (rv == NS_BASE_STREAM_CLOSED) {
|
||||
mIsDone = true;
|
||||
}
|
||||
|
||||
if (Available() || mIsDone) {
|
||||
Http2Stream *consumer = mPushStream->GetConsumerStream();
|
||||
|
||||
if (consumer) {
|
||||
LOG3(("Http2PushTransactionBuffer::WriteSegments notifying connection "
|
||||
"consumer data available 0x%X [%u] done=%d\n",
|
||||
mPushStream->StreamID(), Available(), mIsDone));
|
||||
mPushStream->ConnectPushedStream(consumer);
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Http2PushTransactionBuffer::Http1xTransactionCount()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsHttpRequestHead *
|
||||
Http2PushTransactionBuffer::RequestHead()
|
||||
{
|
||||
if (!mRequestHead)
|
||||
mRequestHead = new nsHttpRequestHead();
|
||||
return mRequestHead;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Http2PushTransactionBuffer::TakeSubTransactions(
|
||||
nsTArray<RefPtr<nsAHttpTransaction> > &outTransactions)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
void
|
||||
Http2PushTransactionBuffer::SetProxyConnectFailed()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Http2PushTransactionBuffer::Close(nsresult reason)
|
||||
{
|
||||
mStatus = reason;
|
||||
mIsDone = true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Http2PushTransactionBuffer::AddTransaction(nsAHttpTransaction *trans)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Http2PushTransactionBuffer::PipelineDepth()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Http2PushTransactionBuffer::SetPipelinePosition(int32_t position)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
Http2PushTransactionBuffer::PipelinePosition()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Http2PushTransactionBuffer::GetBufferedData(char *buf,
|
||||
uint32_t count,
|
||||
uint32_t *countWritten)
|
||||
{
|
||||
*countWritten = std::min(count, static_cast<uint32_t>(Available()));
|
||||
if (*countWritten) {
|
||||
memcpy(buf, &mBufferedHTTP1[mBufferedHTTP1Consumed], *countWritten);
|
||||
mBufferedHTTP1Consumed += *countWritten;
|
||||
}
|
||||
|
||||
// If all the data has been consumed then reset the buffer
|
||||
if (mBufferedHTTP1Consumed == mBufferedHTTP1Used) {
|
||||
mBufferedHTTP1Consumed = 0;
|
||||
mBufferedHTTP1Used = 0;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
129
netwerk/protocol/http/Http2Push.h
Normal file
129
netwerk/protocol/http/Http2Push.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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_Http2Push_Internal_h
|
||||
#define mozilla_net_Http2Push_Internal_h
|
||||
|
||||
// HTTP/2 - RFC 7540
|
||||
// https://www.rfc-editor.org/rfc/rfc7540.txt
|
||||
|
||||
#include "Http2Session.h"
|
||||
#include "Http2Stream.h"
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsHttpRequestHead.h"
|
||||
#include "nsILoadGroup.h"
|
||||
#include "nsIRequestContext.h"
|
||||
#include "nsString.h"
|
||||
#include "PSpdyPush.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class Http2PushTransactionBuffer;
|
||||
|
||||
class Http2PushedStream final : public Http2Stream
|
||||
{
|
||||
public:
|
||||
Http2PushedStream(Http2PushTransactionBuffer *aTransaction,
|
||||
Http2Session *aSession,
|
||||
Http2Stream *aAssociatedStream,
|
||||
uint32_t aID);
|
||||
virtual ~Http2PushedStream() {}
|
||||
|
||||
bool GetPushComplete();
|
||||
|
||||
// The consumer stream is the synthetic pull stream hooked up to this push
|
||||
virtual Http2Stream *GetConsumerStream() override { return mConsumerStream; };
|
||||
|
||||
void SetConsumerStream(Http2Stream *aStream);
|
||||
bool GetHashKey(nsCString &key);
|
||||
|
||||
// override of Http2Stream
|
||||
nsresult ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *) override;
|
||||
nsresult WriteSegments(nsAHttpSegmentWriter *, uint32_t, uint32_t *) override;
|
||||
void AdjustInitialWindow() override;
|
||||
|
||||
nsIRequestContext *RequestContext() override { return mRequestContext; };
|
||||
void ConnectPushedStream(Http2Stream *consumer);
|
||||
|
||||
bool TryOnPush();
|
||||
static bool TestOnPush(Http2Stream *consumer);
|
||||
|
||||
virtual bool DeferCleanup(nsresult status) override;
|
||||
void SetDeferCleanupOnSuccess(bool val) { mDeferCleanupOnSuccess = val; }
|
||||
|
||||
bool IsOrphaned(TimeStamp now);
|
||||
void OnPushFailed() { mDeferCleanupOnPush = false; mOnPushFailed = true; }
|
||||
|
||||
nsresult GetBufferedData(char *buf, uint32_t count, uint32_t *countWritten);
|
||||
|
||||
// overload of Http2Stream
|
||||
virtual bool HasSink() override { return !!mConsumerStream; }
|
||||
|
||||
nsCString &GetRequestString() { return mRequestString; }
|
||||
|
||||
private:
|
||||
|
||||
Http2Stream *mConsumerStream; // paired request stream that consumes from
|
||||
// real http/2 one.. null until a match is made.
|
||||
|
||||
nsCOMPtr<nsIRequestContext> mRequestContext;
|
||||
|
||||
nsAHttpTransaction *mAssociatedTransaction;
|
||||
|
||||
Http2PushTransactionBuffer *mBufferedPush;
|
||||
mozilla::TimeStamp mLastRead;
|
||||
|
||||
nsCString mHashKey;
|
||||
nsresult mStatus;
|
||||
bool mPushCompleted; // server push FIN received
|
||||
bool mDeferCleanupOnSuccess;
|
||||
|
||||
// mDeferCleanupOnPush prevents Http2Session::CleanupStream() from
|
||||
// destroying the push stream on an error code during the period between
|
||||
// when we need to do OnPush() on another thread and the time it takes
|
||||
// for that event to create a synthetic pull stream attached to this
|
||||
// object. That synthetic pull will become mConsuemerStream.
|
||||
// Ths is essentially a delete protecting reference.
|
||||
bool mDeferCleanupOnPush;
|
||||
bool mOnPushFailed;
|
||||
nsCString mRequestString;
|
||||
|
||||
};
|
||||
|
||||
class Http2PushTransactionBuffer final : public nsAHttpTransaction
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSAHTTPTRANSACTION
|
||||
|
||||
Http2PushTransactionBuffer();
|
||||
|
||||
nsresult GetBufferedData(char *buf, uint32_t count, uint32_t *countWritten);
|
||||
void SetPushStream(Http2PushedStream *stream) { mPushStream = stream; }
|
||||
|
||||
private:
|
||||
virtual ~Http2PushTransactionBuffer();
|
||||
|
||||
const static uint32_t kDefaultBufferSize = 4096;
|
||||
|
||||
nsresult mStatus;
|
||||
nsHttpRequestHead *mRequestHead;
|
||||
Http2PushedStream *mPushStream;
|
||||
bool mIsDone;
|
||||
|
||||
UniquePtr<char[]> mBufferedHTTP1;
|
||||
uint32_t mBufferedHTTP1Size;
|
||||
uint32_t mBufferedHTTP1Used;
|
||||
uint32_t mBufferedHTTP1Consumed;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_Http2Push_Internal_h
|
||||
3884
netwerk/protocol/http/Http2Session.cpp
Normal file
3884
netwerk/protocol/http/Http2Session.cpp
Normal file
File diff suppressed because it is too large
Load diff
508
netwerk/protocol/http/Http2Session.h
Normal file
508
netwerk/protocol/http/Http2Session.h
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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_Http2Session_h
|
||||
#define mozilla_net_Http2Session_h
|
||||
|
||||
// HTTP/2 - RFC 7540
|
||||
// https://www.rfc-editor.org/rfc/rfc7540.txt
|
||||
|
||||
#include "ASpdySession.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsAHttpConnection.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsDeque.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
#include "Http2Compression.h"
|
||||
|
||||
class nsISocketTransport;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class Http2PushedStream;
|
||||
class Http2Stream;
|
||||
class nsHttpTransaction;
|
||||
|
||||
class Http2Session final : public ASpdySession
|
||||
, public nsAHttpConnection
|
||||
, public nsAHttpSegmentReader
|
||||
, public nsAHttpSegmentWriter
|
||||
{
|
||||
~Http2Session();
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSAHTTPTRANSACTION
|
||||
NS_DECL_NSAHTTPCONNECTION(mConnection)
|
||||
NS_DECL_NSAHTTPSEGMENTREADER
|
||||
NS_DECL_NSAHTTPSEGMENTWRITER
|
||||
|
||||
Http2Session(nsISocketTransport *, uint32_t version);
|
||||
|
||||
bool AddStream(nsAHttpTransaction *, int32_t,
|
||||
bool, nsIInterfaceRequestor *) override;
|
||||
bool CanReuse() override { return !mShouldGoAway && !mClosed; }
|
||||
bool RoomForMoreStreams() override;
|
||||
|
||||
// When the connection is active this is called up to once every 1 second
|
||||
// return the interval (in seconds) that the connection next wants to
|
||||
// have this invoked. It might happen sooner depending on the needs of
|
||||
// other connections.
|
||||
uint32_t ReadTimeoutTick(PRIntervalTime now) override;
|
||||
|
||||
// Idle time represents time since "goodput".. e.g. a data or header frame
|
||||
PRIntervalTime IdleTime() override;
|
||||
|
||||
// Registering with a newID of 0 means pick the next available odd ID
|
||||
uint32_t RegisterStreamID(Http2Stream *, uint32_t aNewID = 0);
|
||||
|
||||
/*
|
||||
HTTP/2 framing
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Length (16) | Type (8) | Flags (8) |
|
||||
+-+-------------+---------------+-------------------------------+
|
||||
|R| Stream Identifier (31) |
|
||||
+-+-------------------------------------------------------------+
|
||||
| Frame Data (0...) ...
|
||||
+---------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
enum FrameType {
|
||||
FRAME_TYPE_DATA = 0x0,
|
||||
FRAME_TYPE_HEADERS = 0x1,
|
||||
FRAME_TYPE_PRIORITY = 0x2,
|
||||
FRAME_TYPE_RST_STREAM = 0x3,
|
||||
FRAME_TYPE_SETTINGS = 0x4,
|
||||
FRAME_TYPE_PUSH_PROMISE = 0x5,
|
||||
FRAME_TYPE_PING = 0x6,
|
||||
FRAME_TYPE_GOAWAY = 0x7,
|
||||
FRAME_TYPE_WINDOW_UPDATE = 0x8,
|
||||
FRAME_TYPE_CONTINUATION = 0x9,
|
||||
FRAME_TYPE_ALTSVC = 0xA,
|
||||
FRAME_TYPE_LAST = 0xB
|
||||
};
|
||||
|
||||
// NO_ERROR is a macro defined on windows, so we'll name the HTTP2 goaway
|
||||
// code NO_ERROR to be NO_HTTP_ERROR
|
||||
enum errorType {
|
||||
NO_HTTP_ERROR = 0,
|
||||
PROTOCOL_ERROR = 1,
|
||||
INTERNAL_ERROR = 2,
|
||||
FLOW_CONTROL_ERROR = 3,
|
||||
SETTINGS_TIMEOUT_ERROR = 4,
|
||||
STREAM_CLOSED_ERROR = 5,
|
||||
FRAME_SIZE_ERROR = 6,
|
||||
REFUSED_STREAM_ERROR = 7,
|
||||
CANCEL_ERROR = 8,
|
||||
COMPRESSION_ERROR = 9,
|
||||
CONNECT_ERROR = 10,
|
||||
ENHANCE_YOUR_CALM = 11,
|
||||
INADEQUATE_SECURITY = 12,
|
||||
HTTP_1_1_REQUIRED = 13,
|
||||
UNASSIGNED = 31
|
||||
};
|
||||
|
||||
// These are frame flags. If they, or other undefined flags, are
|
||||
// used on frames other than the comments indicate they MUST be ignored.
|
||||
const static uint8_t kFlag_END_STREAM = 0x01; // data, headers
|
||||
const static uint8_t kFlag_END_HEADERS = 0x04; // headers, continuation
|
||||
const static uint8_t kFlag_END_PUSH_PROMISE = 0x04; // push promise
|
||||
const static uint8_t kFlag_ACK = 0x01; // ping and settings
|
||||
const static uint8_t kFlag_PADDED = 0x08; // data, headers, push promise, continuation
|
||||
const static uint8_t kFlag_PRIORITY = 0x20; // headers
|
||||
|
||||
enum {
|
||||
SETTINGS_TYPE_HEADER_TABLE_SIZE = 1, // compression table size
|
||||
SETTINGS_TYPE_ENABLE_PUSH = 2, // can be used to disable push
|
||||
SETTINGS_TYPE_MAX_CONCURRENT = 3, // streams recvr allowed to initiate
|
||||
SETTINGS_TYPE_INITIAL_WINDOW = 4, // bytes for flow control default
|
||||
SETTINGS_TYPE_MAX_FRAME_SIZE = 5 // max frame size settings sender allows receipt of
|
||||
};
|
||||
|
||||
// This should be big enough to hold all of your control packets,
|
||||
// but if it needs to grow for huge headers it can do so dynamically.
|
||||
const static uint32_t kDefaultBufferSize = 2048;
|
||||
|
||||
// kDefaultQueueSize must be >= other queue size constants
|
||||
const static uint32_t kDefaultQueueSize = 32768;
|
||||
const static uint32_t kQueueMinimumCleanup = 24576;
|
||||
const static uint32_t kQueueTailRoom = 4096;
|
||||
const static uint32_t kQueueReserved = 1024;
|
||||
|
||||
const static uint32_t kMaxStreamID = 0x7800000;
|
||||
|
||||
// This is a sentinel for a deleted stream. It is not a valid
|
||||
// 31 bit stream ID.
|
||||
const static uint32_t kDeadStreamID = 0xffffdead;
|
||||
|
||||
// below the emergency threshold of local window we ack every received
|
||||
// byte. Above that we coalesce bytes into the MinimumToAck size.
|
||||
const static int32_t kEmergencyWindowThreshold = 256 * 1024;
|
||||
const static uint32_t kMinimumToAck = 4 * 1024 * 1024;
|
||||
|
||||
// The default rwin is 64KB - 1 unless updated by a settings frame
|
||||
const static uint32_t kDefaultRwin = 65535;
|
||||
|
||||
// We limit frames to 2^14 bytes of length in order to preserve responsiveness
|
||||
// This is the smallest allowed value for SETTINGS_MAX_FRAME_SIZE
|
||||
const static uint32_t kMaxFrameData = 0x4000;
|
||||
|
||||
const static uint8_t kFrameLengthBytes = 3;
|
||||
const static uint8_t kFrameStreamIDBytes = 4;
|
||||
const static uint8_t kFrameFlagBytes = 1;
|
||||
const static uint8_t kFrameTypeBytes = 1;
|
||||
const static uint8_t kFrameHeaderBytes = kFrameLengthBytes + kFrameFlagBytes +
|
||||
kFrameTypeBytes + kFrameStreamIDBytes;
|
||||
|
||||
enum {
|
||||
kLeaderGroupID = 0x3,
|
||||
kOtherGroupID = 0x5,
|
||||
kBackgroundGroupID = 0x7,
|
||||
kSpeculativeGroupID = 0x9,
|
||||
kFollowerGroupID = 0xB
|
||||
};
|
||||
|
||||
static nsresult RecvHeaders(Http2Session *);
|
||||
static nsresult RecvPriority(Http2Session *);
|
||||
static nsresult RecvRstStream(Http2Session *);
|
||||
static nsresult RecvSettings(Http2Session *);
|
||||
static nsresult RecvPushPromise(Http2Session *);
|
||||
static nsresult RecvPing(Http2Session *);
|
||||
static nsresult RecvGoAway(Http2Session *);
|
||||
static nsresult RecvWindowUpdate(Http2Session *);
|
||||
static nsresult RecvContinuation(Http2Session *);
|
||||
static nsresult RecvAltSvc(Http2Session *);
|
||||
|
||||
char *EnsureOutputBuffer(uint32_t needed);
|
||||
|
||||
template<typename charType>
|
||||
void CreateFrameHeader(charType dest, uint16_t frameLength,
|
||||
uint8_t frameType, uint8_t frameFlags,
|
||||
uint32_t streamID);
|
||||
|
||||
// For writing the data stream to LOG4
|
||||
static void LogIO(Http2Session *, Http2Stream *, const char *,
|
||||
const char *, uint32_t);
|
||||
|
||||
// overload of nsAHttpConnection
|
||||
void TransactionHasDataToWrite(nsAHttpTransaction *) override;
|
||||
void TransactionHasDataToRecv(nsAHttpTransaction *) override;
|
||||
|
||||
// a similar version for Http2Stream
|
||||
void TransactionHasDataToWrite(Http2Stream *);
|
||||
|
||||
// an overload of nsAHttpSegementReader
|
||||
virtual nsresult CommitToSegmentSize(uint32_t size, bool forceCommitment) override;
|
||||
nsresult BufferOutput(const char *, uint32_t, uint32_t *);
|
||||
void FlushOutputQueue();
|
||||
uint32_t AmountOfOutputBuffered() { return mOutputQueueUsed - mOutputQueueSent; }
|
||||
|
||||
uint32_t GetServerInitialStreamWindow() { return mServerInitialStreamWindow; }
|
||||
|
||||
bool TryToActivate(Http2Stream *stream);
|
||||
void ConnectPushedStream(Http2Stream *stream);
|
||||
void ConnectSlowConsumer(Http2Stream *stream);
|
||||
|
||||
nsresult ConfirmTLSProfile();
|
||||
static bool ALPNCallback(nsISupports *securityInfo);
|
||||
|
||||
uint64_t Serial() { return mSerial; }
|
||||
|
||||
void PrintDiagnostics (nsCString &log) override;
|
||||
|
||||
// Streams need access to these
|
||||
uint32_t SendingChunkSize() { return mSendingChunkSize; }
|
||||
uint32_t PushAllowance() { return mPushAllowance; }
|
||||
Http2Compressor *Compressor() { return &mCompressor; }
|
||||
nsISocketTransport *SocketTransport() { return mSocketTransport; }
|
||||
int64_t ServerSessionWindow() { return mServerSessionWindow; }
|
||||
void DecrementServerSessionWindow (uint32_t bytes) { mServerSessionWindow -= bytes; }
|
||||
uint32_t InitialRwin() { return mInitialRwin; }
|
||||
|
||||
void SendPing() override;
|
||||
bool MaybeReTunnel(nsAHttpTransaction *) override;
|
||||
bool UseH2Deps() { return mUseH2Deps; }
|
||||
|
||||
// overload of nsAHttpTransaction
|
||||
nsresult ReadSegmentsAgain(nsAHttpSegmentReader *, uint32_t, uint32_t *, bool *) override final;
|
||||
nsresult WriteSegmentsAgain(nsAHttpSegmentWriter *, uint32_t , uint32_t *, bool *) override final;
|
||||
|
||||
private:
|
||||
|
||||
// These internal states do not correspond to the states of the HTTP/2 specification
|
||||
enum internalStateType {
|
||||
BUFFERING_OPENING_SETTINGS,
|
||||
BUFFERING_FRAME_HEADER,
|
||||
BUFFERING_CONTROL_FRAME,
|
||||
PROCESSING_DATA_FRAME_PADDING_CONTROL,
|
||||
PROCESSING_DATA_FRAME,
|
||||
DISCARDING_DATA_FRAME_PADDING,
|
||||
DISCARDING_DATA_FRAME,
|
||||
PROCESSING_COMPLETE_HEADERS,
|
||||
PROCESSING_CONTROL_RST_STREAM,
|
||||
NOT_USING_NETWORK
|
||||
};
|
||||
|
||||
static const uint8_t kMagicHello[24];
|
||||
|
||||
nsresult ResponseHeadersComplete();
|
||||
uint32_t GetWriteQueueSize();
|
||||
void ChangeDownstreamState(enum internalStateType);
|
||||
void ResetDownstreamState();
|
||||
nsresult ReadyToProcessDataFrame(enum internalStateType);
|
||||
nsresult UncompressAndDiscard(bool);
|
||||
void GeneratePing(bool);
|
||||
void GenerateSettingsAck();
|
||||
void GeneratePriority(uint32_t, uint8_t);
|
||||
void GenerateRstStream(uint32_t, uint32_t);
|
||||
void GenerateGoAway(uint32_t);
|
||||
void CleanupStream(Http2Stream *, nsresult, errorType);
|
||||
void CleanupStream(uint32_t, nsresult, errorType);
|
||||
void CloseStream(Http2Stream *, nsresult);
|
||||
void SendHello();
|
||||
void RemoveStreamFromQueues(Http2Stream *);
|
||||
nsresult ParsePadding(uint8_t &, uint16_t &);
|
||||
|
||||
void SetWriteCallbacks();
|
||||
void RealignOutputQueue();
|
||||
|
||||
void ProcessPending();
|
||||
nsresult ProcessConnectedPush(Http2Stream *, nsAHttpSegmentWriter *,
|
||||
uint32_t, uint32_t *);
|
||||
nsresult ProcessSlowConsumer(Http2Stream *, nsAHttpSegmentWriter *,
|
||||
uint32_t, uint32_t *);
|
||||
|
||||
nsresult SetInputFrameDataStream(uint32_t);
|
||||
void CreatePriorityNode(uint32_t, uint32_t, uint8_t, const char *);
|
||||
bool VerifyStream(Http2Stream *, uint32_t);
|
||||
void SetNeedsCleanup();
|
||||
|
||||
void UpdateLocalRwin(Http2Stream *stream, uint32_t bytes);
|
||||
void UpdateLocalStreamWindow(Http2Stream *stream, uint32_t bytes);
|
||||
void UpdateLocalSessionWindow(uint32_t bytes);
|
||||
|
||||
void MaybeDecrementConcurrent(Http2Stream *stream);
|
||||
bool RoomForMoreConcurrent();
|
||||
void IncrementConcurrent(Http2Stream *stream);
|
||||
void QueueStream(Http2Stream *stream);
|
||||
|
||||
// a wrapper for all calls to the nshttpconnection level segment writer. Used
|
||||
// to track network I/O for timeout purposes
|
||||
nsresult NetworkRead(nsAHttpSegmentWriter *, char *, uint32_t, uint32_t *);
|
||||
|
||||
void Shutdown();
|
||||
|
||||
// This is intended to be nsHttpConnectionMgr:nsConnectionHandle taken
|
||||
// from the first transaction on this session. That object contains the
|
||||
// pointer to the real network-level nsHttpConnection object.
|
||||
RefPtr<nsAHttpConnection> mConnection;
|
||||
|
||||
// The underlying socket transport object is needed to propogate some events
|
||||
nsISocketTransport *mSocketTransport;
|
||||
|
||||
// These are temporary state variables to hold the argument to
|
||||
// Read/WriteSegments so it can be accessed by On(read/write)segment
|
||||
// further up the stack.
|
||||
nsAHttpSegmentReader *mSegmentReader;
|
||||
nsAHttpSegmentWriter *mSegmentWriter;
|
||||
|
||||
uint32_t mSendingChunkSize; /* the transmission chunk size */
|
||||
uint32_t mNextStreamID; /* 24 bits */
|
||||
uint32_t mLastPushedID;
|
||||
uint32_t mConcurrentHighWater; /* max parallelism on session */
|
||||
uint32_t mPushAllowance; /* rwin for unmatched pushes */
|
||||
|
||||
internalStateType mDownstreamState; /* in frame, between frames, etc.. */
|
||||
|
||||
// Maintain 2 indexes - one by stream ID, one by transaction pointer.
|
||||
// There are also several lists of streams: ready to write, queued due to
|
||||
// max parallelism, streams that need to force a read for push, and the full
|
||||
// set of pushed streams.
|
||||
// The objects are not ref counted - they get destroyed
|
||||
// by the nsClassHashtable implementation when they are removed from
|
||||
// the transaction hash.
|
||||
nsDataHashtable<nsUint32HashKey, Http2Stream *> mStreamIDHash;
|
||||
nsClassHashtable<nsPtrHashKey<nsAHttpTransaction>,
|
||||
Http2Stream> mStreamTransactionHash;
|
||||
|
||||
nsDeque mReadyForWrite;
|
||||
nsDeque mQueuedStreams;
|
||||
nsDeque mPushesReadyForRead;
|
||||
nsDeque mSlowConsumersReadyForRead;
|
||||
nsTArray<Http2PushedStream *> mPushedStreams;
|
||||
|
||||
// Compression contexts for header transport.
|
||||
// HTTP/2 compresses only HTTP headers and does not reset the context in between
|
||||
// frames. Even data that is not associated with a stream (e.g invalid
|
||||
// stream ID) is passed through these contexts to keep the compression
|
||||
// context correct.
|
||||
Http2Compressor mCompressor;
|
||||
Http2Decompressor mDecompressor;
|
||||
nsCString mDecompressBuffer;
|
||||
|
||||
// mInputFrameBuffer is used to store received control packets and the 8 bytes
|
||||
// of header on data packets
|
||||
uint32_t mInputFrameBufferSize; // buffer allocation
|
||||
uint32_t mInputFrameBufferUsed; // amt of allocation used
|
||||
UniquePtr<char[]> mInputFrameBuffer;
|
||||
|
||||
// mInputFrameDataSize/Read are used for tracking the amount of data consumed
|
||||
// in a frame after the 8 byte header. Control frames are always fully buffered
|
||||
// and the fixed 8 byte leading header is at mInputFrameBuffer + 0, the first
|
||||
// data byte (i.e. the first settings/goaway/etc.. specific byte) is at
|
||||
// mInputFrameBuffer + 8
|
||||
// The frame size is mInputFrameDataSize + the constant 8 byte header
|
||||
uint32_t mInputFrameDataSize;
|
||||
uint32_t mInputFrameDataRead;
|
||||
bool mInputFrameFinal; // This frame was marked FIN
|
||||
uint8_t mInputFrameType;
|
||||
uint8_t mInputFrameFlags;
|
||||
uint32_t mInputFrameID;
|
||||
uint16_t mPaddingLength;
|
||||
|
||||
// When a frame has been received that is addressed to a particular stream
|
||||
// (e.g. a data frame after the stream-id has been decoded), this points
|
||||
// to the stream.
|
||||
Http2Stream *mInputFrameDataStream;
|
||||
|
||||
// mNeedsCleanup is a state variable to defer cleanup of a closed stream
|
||||
// If needed, It is set in session::OnWriteSegments() and acted on and
|
||||
// cleared when the stack returns to session::WriteSegments(). The stream
|
||||
// cannot be destroyed directly out of OnWriteSegments because
|
||||
// stream::writeSegments() is on the stack at that time.
|
||||
Http2Stream *mNeedsCleanup;
|
||||
|
||||
// This reason code in the last processed RESET frame
|
||||
uint32_t mDownstreamRstReason;
|
||||
|
||||
// When HEADERS/PROMISE are chained together, this is the expected ID of the next
|
||||
// recvd frame which must be the same type
|
||||
uint32_t mExpectedHeaderID;
|
||||
uint32_t mExpectedPushPromiseID;
|
||||
uint32_t mContinuedPromiseStream;
|
||||
|
||||
// for the conversion of downstream http headers into http/2 formatted headers
|
||||
// The data here does not persist between frames
|
||||
nsCString mFlatHTTPResponseHeaders;
|
||||
uint32_t mFlatHTTPResponseHeadersOut;
|
||||
|
||||
// when set, the session will go away when it reaches 0 streams. This flag
|
||||
// is set when: the stream IDs are running out (at either the client or the
|
||||
// server), when DontReuse() is called, a RST that is not specific to a
|
||||
// particular stream is received, a GOAWAY frame has been received from
|
||||
// the server.
|
||||
bool mShouldGoAway;
|
||||
|
||||
// the session has received a nsAHttpTransaction::Close() call
|
||||
bool mClosed;
|
||||
|
||||
// the session received a GoAway frame with a valid GoAwayID
|
||||
bool mCleanShutdown;
|
||||
|
||||
// The TLS comlpiance checks are not done in the ctor beacuse of bad
|
||||
// exception handling - so we do them at IO time and cache the result
|
||||
bool mTLSProfileConfirmed;
|
||||
|
||||
// A specifc reason code for the eventual GoAway frame. If set to NO_HTTP_ERROR
|
||||
// only NO_HTTP_ERROR, PROTOCOL_ERROR, or INTERNAL_ERROR will be sent.
|
||||
errorType mGoAwayReason;
|
||||
|
||||
// The error code sent/received on the session goaway frame. UNASSIGNED/31
|
||||
// if not transmitted.
|
||||
int32_t mClientGoAwayReason;
|
||||
int32_t mPeerGoAwayReason;
|
||||
|
||||
// If a GoAway message was received this is the ID of the last valid
|
||||
// stream. 0 otherwise. (0 is never a valid stream id.)
|
||||
uint32_t mGoAwayID;
|
||||
|
||||
// The last stream processed ID we will send in our GoAway frame.
|
||||
uint32_t mOutgoingGoAwayID;
|
||||
|
||||
// The limit on number of concurrent streams for this session. Normally it
|
||||
// is basically unlimited, but the SETTINGS control message from the
|
||||
// server might bring it down.
|
||||
uint32_t mMaxConcurrent;
|
||||
|
||||
// The actual number of concurrent streams at this moment. Generally below
|
||||
// mMaxConcurrent, but the max can be lowered in real time to a value
|
||||
// below the current value
|
||||
uint32_t mConcurrent;
|
||||
|
||||
// The number of server initiated promises, tracked for telemetry
|
||||
uint32_t mServerPushedResources;
|
||||
|
||||
// The server rwin for new streams as determined from a SETTINGS frame
|
||||
uint32_t mServerInitialStreamWindow;
|
||||
|
||||
// The Local Session window is how much data the server is allowed to send
|
||||
// (across all streams) without getting a window update to stream 0. It is
|
||||
// signed because asynchronous changes via SETTINGS can drive it negative.
|
||||
int64_t mLocalSessionWindow;
|
||||
|
||||
// The Remote Session Window is how much data the client is allowed to send
|
||||
// (across all streams) without receiving a window update to stream 0. It is
|
||||
// signed because asynchronous changes via SETTINGS can drive it negative.
|
||||
int64_t mServerSessionWindow;
|
||||
|
||||
// The initial value of the local stream and session window
|
||||
uint32_t mInitialRwin;
|
||||
|
||||
// This is a output queue of bytes ready to be written to the SSL stream.
|
||||
// When that streams returns WOULD_BLOCK on direct write the bytes get
|
||||
// coalesced together here. This results in larger writes to the SSL layer.
|
||||
// The buffer is not dynamically grown to accomodate stream writes, but
|
||||
// does expand to accept infallible session wide frames like GoAway and RST.
|
||||
uint32_t mOutputQueueSize;
|
||||
uint32_t mOutputQueueUsed;
|
||||
uint32_t mOutputQueueSent;
|
||||
UniquePtr<char[]> mOutputQueueBuffer;
|
||||
|
||||
PRIntervalTime mPingThreshold;
|
||||
PRIntervalTime mLastReadEpoch; // used for ping timeouts
|
||||
PRIntervalTime mLastDataReadEpoch; // used for IdleTime()
|
||||
PRIntervalTime mPingSentEpoch;
|
||||
|
||||
PRIntervalTime mPreviousPingThreshold; // backup for the former value
|
||||
bool mPreviousUsed; // true when backup is used
|
||||
|
||||
// used as a temporary buffer while enumerating the stream hash during GoAway
|
||||
nsDeque mGoAwayStreamsToRestart;
|
||||
|
||||
// Each session gets a unique serial number because the push cache is correlated
|
||||
// by the load group and the serial number can be used as part of the cache key
|
||||
// to make sure streams aren't shared across sessions.
|
||||
uint64_t mSerial;
|
||||
|
||||
// If push is disabled, we want to be able to send PROTOCOL_ERRORs if we
|
||||
// receive a PUSH_PROMISE, but we have to wait for the SETTINGS ACK before
|
||||
// we can actually tell the other end to go away. These help us keep track
|
||||
// of that state so we can behave appropriately.
|
||||
bool mWaitingForSettingsAck;
|
||||
bool mGoAwayOnPush;
|
||||
|
||||
bool mUseH2Deps;
|
||||
|
||||
private:
|
||||
/// connect tunnels
|
||||
void DispatchOnTunnel(nsAHttpTransaction *, nsIInterfaceRequestor *);
|
||||
void CreateTunnel(nsHttpTransaction *, nsHttpConnectionInfo *, nsIInterfaceRequestor *);
|
||||
void RegisterTunnel(Http2Stream *);
|
||||
void UnRegisterTunnel(Http2Stream *);
|
||||
uint32_t FindTunnelCount(nsHttpConnectionInfo *);
|
||||
nsDataHashtable<nsCStringHashKey, uint32_t> mTunnelHash;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_Http2Session_h
|
||||
1472
netwerk/protocol/http/Http2Stream.cpp
Normal file
1472
netwerk/protocol/http/Http2Stream.cpp
Normal file
File diff suppressed because it is too large
Load diff
346
netwerk/protocol/http/Http2Stream.h
Normal file
346
netwerk/protocol/http/Http2Stream.h
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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_Http2Stream_h
|
||||
#define mozilla_net_Http2Stream_h
|
||||
|
||||
// HTTP/2 - RFC7540
|
||||
// https://www.rfc-editor.org/rfc/rfc7540.txt
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsAHttpTransaction.h"
|
||||
#include "nsISupportsPriority.h"
|
||||
#include "SimpleBuffer.h"
|
||||
|
||||
class nsIInputStream;
|
||||
class nsIOutputStream;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class nsStandardURL;
|
||||
class Http2Session;
|
||||
class Http2Decompressor;
|
||||
|
||||
class Http2Stream
|
||||
: public nsAHttpSegmentReader
|
||||
, public nsAHttpSegmentWriter
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSAHTTPSEGMENTREADER
|
||||
NS_DECL_NSAHTTPSEGMENTWRITER
|
||||
|
||||
enum stateType {
|
||||
IDLE,
|
||||
RESERVED_BY_REMOTE,
|
||||
OPEN,
|
||||
CLOSED_BY_LOCAL,
|
||||
CLOSED_BY_REMOTE,
|
||||
CLOSED
|
||||
};
|
||||
|
||||
const static int32_t kNormalPriority = 0x1000;
|
||||
const static int32_t kWorstPriority = kNormalPriority + nsISupportsPriority::PRIORITY_LOWEST;
|
||||
const static int32_t kBestPriority = kNormalPriority + nsISupportsPriority::PRIORITY_HIGHEST;
|
||||
|
||||
Http2Stream(nsAHttpTransaction *, Http2Session *, int32_t);
|
||||
|
||||
uint32_t StreamID() { return mStreamID; }
|
||||
Http2PushedStream *PushSource() { return mPushSource; }
|
||||
|
||||
stateType HTTPState() { return mState; }
|
||||
void SetHTTPState(stateType val) { mState = val; }
|
||||
|
||||
virtual nsresult ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *);
|
||||
virtual nsresult WriteSegments(nsAHttpSegmentWriter *, uint32_t, uint32_t *);
|
||||
virtual bool DeferCleanup(nsresult status);
|
||||
|
||||
// The consumer stream is the synthetic pull stream hooked up to this stream
|
||||
// http2PushedStream overrides it
|
||||
virtual Http2Stream *GetConsumerStream() { return nullptr; };
|
||||
|
||||
const nsAFlatCString &Origin() const { return mOrigin; }
|
||||
const nsAFlatCString &Host() const { return mHeaderHost; }
|
||||
const nsAFlatCString &Path() const { return mHeaderPath; }
|
||||
|
||||
bool RequestBlockedOnRead()
|
||||
{
|
||||
return static_cast<bool>(mRequestBlockedOnRead);
|
||||
}
|
||||
|
||||
bool HasRegisteredID() { return mStreamID != 0; }
|
||||
|
||||
nsAHttpTransaction *Transaction() { return mTransaction; }
|
||||
virtual nsIRequestContext *RequestContext()
|
||||
{
|
||||
return mTransaction ? mTransaction->RequestContext() : nullptr;
|
||||
}
|
||||
|
||||
void Close(nsresult reason);
|
||||
void SetResponseIsComplete();
|
||||
|
||||
void SetRecvdFin(bool aStatus);
|
||||
bool RecvdFin() { return mRecvdFin; }
|
||||
|
||||
void SetRecvdData(bool aStatus) { mReceivedData = aStatus ? 1 : 0; }
|
||||
bool RecvdData() { return mReceivedData; }
|
||||
|
||||
void SetSentFin(bool aStatus);
|
||||
bool SentFin() { return mSentFin; }
|
||||
|
||||
void SetRecvdReset(bool aStatus);
|
||||
bool RecvdReset() { return mRecvdReset; }
|
||||
|
||||
void SetSentReset(bool aStatus);
|
||||
bool SentReset() { return mSentReset; }
|
||||
|
||||
void SetQueued(bool aStatus) { mQueued = aStatus ? 1 : 0; }
|
||||
bool Queued() { return mQueued; }
|
||||
|
||||
void SetCountAsActive(bool aStatus) { mCountAsActive = aStatus ? 1 : 0; }
|
||||
bool CountAsActive() { return mCountAsActive; }
|
||||
|
||||
void SetAllHeadersReceived();
|
||||
void UnsetAllHeadersReceived() { mAllHeadersReceived = 0; }
|
||||
bool AllHeadersReceived() { return mAllHeadersReceived; }
|
||||
|
||||
void UpdateTransportSendEvents(uint32_t count);
|
||||
void UpdateTransportReadEvents(uint32_t count);
|
||||
|
||||
// NS_ERROR_ABORT terminates stream, other failure terminates session
|
||||
nsresult ConvertResponseHeaders(Http2Decompressor *, nsACString &,
|
||||
nsACString &, int32_t &);
|
||||
nsresult ConvertPushHeaders(Http2Decompressor *, nsACString &, nsACString &);
|
||||
|
||||
bool AllowFlowControlledWrite();
|
||||
void UpdateServerReceiveWindow(int32_t delta);
|
||||
int64_t ServerReceiveWindow() { return mServerReceiveWindow; }
|
||||
|
||||
void DecrementClientReceiveWindow(uint32_t delta) {
|
||||
mClientReceiveWindow -= delta;
|
||||
mLocalUnacked += delta;
|
||||
}
|
||||
|
||||
void IncrementClientReceiveWindow(uint32_t delta) {
|
||||
mClientReceiveWindow += delta;
|
||||
mLocalUnacked -= delta;
|
||||
}
|
||||
|
||||
uint64_t LocalUnAcked();
|
||||
int64_t ClientReceiveWindow() { return mClientReceiveWindow; }
|
||||
|
||||
bool BlockedOnRwin() { return mBlockedOnRwin; }
|
||||
|
||||
uint32_t Priority() { return mPriority; }
|
||||
void SetPriority(uint32_t);
|
||||
void SetPriorityDependency(uint32_t, uint8_t, bool);
|
||||
void UpdatePriorityDependency();
|
||||
|
||||
// A pull stream has an implicit sink, a pushed stream has a sink
|
||||
// once it is matched to a pull stream.
|
||||
virtual bool HasSink() { return true; }
|
||||
|
||||
virtual ~Http2Stream();
|
||||
|
||||
Http2Session *Session() { return mSession; }
|
||||
|
||||
static nsresult MakeOriginURL(const nsACString &origin,
|
||||
RefPtr<nsStandardURL> &url);
|
||||
|
||||
static nsresult MakeOriginURL(const nsACString &scheme,
|
||||
const nsACString &origin,
|
||||
RefPtr<nsStandardURL> &url);
|
||||
|
||||
protected:
|
||||
static void CreatePushHashKey(const nsCString &scheme,
|
||||
const nsCString &hostHeader,
|
||||
uint64_t serial,
|
||||
const nsCSubstring &pathInfo,
|
||||
nsCString &outOrigin,
|
||||
nsCString &outKey);
|
||||
|
||||
// These internal states track request generation
|
||||
enum upstreamStateType {
|
||||
GENERATING_HEADERS,
|
||||
GENERATING_BODY,
|
||||
SENDING_BODY,
|
||||
SENDING_FIN_STREAM,
|
||||
UPSTREAM_COMPLETE
|
||||
};
|
||||
|
||||
uint32_t mStreamID;
|
||||
|
||||
// The session that this stream is a subset of
|
||||
Http2Session *mSession;
|
||||
|
||||
// These are temporary state variables to hold the argument to
|
||||
// Read/WriteSegments so it can be accessed by On(read/write)segment
|
||||
// further up the stack.
|
||||
nsAHttpSegmentReader *mSegmentReader;
|
||||
nsAHttpSegmentWriter *mSegmentWriter;
|
||||
|
||||
nsCString mOrigin;
|
||||
nsCString mHeaderHost;
|
||||
nsCString mHeaderScheme;
|
||||
nsCString mHeaderPath;
|
||||
|
||||
// Each stream goes from generating_headers to upstream_complete, perhaps
|
||||
// looping on multiple instances of generating_body and
|
||||
// sending_body for each frame in the upload.
|
||||
enum upstreamStateType mUpstreamState;
|
||||
|
||||
// The HTTP/2 state for the stream from section 5.1
|
||||
enum stateType mState;
|
||||
|
||||
// Flag is set when all http request headers have been read ID is not stable
|
||||
uint32_t mRequestHeadersDone : 1;
|
||||
|
||||
// Flag is set when ID is stable and concurrency limits are met
|
||||
uint32_t mOpenGenerated : 1;
|
||||
|
||||
// Flag is set when all http response headers have been read
|
||||
uint32_t mAllHeadersReceived : 1;
|
||||
|
||||
// Flag is set when stream is queued inside the session due to
|
||||
// concurrency limits being exceeded
|
||||
uint32_t mQueued : 1;
|
||||
|
||||
void ChangeState(enum upstreamStateType);
|
||||
|
||||
virtual void AdjustInitialWindow();
|
||||
nsresult TransmitFrame(const char *, uint32_t *, bool forceCommitment);
|
||||
|
||||
private:
|
||||
friend class nsAutoPtr<Http2Stream>;
|
||||
|
||||
nsresult ParseHttpRequestHeaders(const char *, uint32_t, uint32_t *);
|
||||
nsresult GenerateOpen();
|
||||
|
||||
void AdjustPushedPriority();
|
||||
void GenerateDataFrameHeader(uint32_t, bool);
|
||||
|
||||
nsresult BufferInput(uint32_t , uint32_t *);
|
||||
|
||||
// The underlying HTTP transaction. This pointer is used as the key
|
||||
// in the Http2Session mStreamTransactionHash so it is important to
|
||||
// keep a reference to it as long as this stream is a member of that hash.
|
||||
// (i.e. don't change it or release it after it is set in the ctor).
|
||||
RefPtr<nsAHttpTransaction> mTransaction;
|
||||
|
||||
// The underlying socket transport object is needed to propogate some events
|
||||
nsISocketTransport *mSocketTransport;
|
||||
|
||||
// The quanta upstream data frames are chopped into
|
||||
uint32_t mChunkSize;
|
||||
|
||||
// Flag is set when the HTTP processor has more data to send
|
||||
// but has blocked in doing so.
|
||||
uint32_t mRequestBlockedOnRead : 1;
|
||||
|
||||
// Flag is set after the response frame bearing the fin bit has
|
||||
// been processed. (i.e. after the server has closed).
|
||||
uint32_t mRecvdFin : 1;
|
||||
|
||||
// Flag is set after 1st DATA frame has been passed to stream
|
||||
uint32_t mReceivedData : 1;
|
||||
|
||||
// Flag is set after RST_STREAM has been received for this stream
|
||||
uint32_t mRecvdReset : 1;
|
||||
|
||||
// Flag is set after RST_STREAM has been generated for this stream
|
||||
uint32_t mSentReset : 1;
|
||||
|
||||
// Flag is set when stream is counted towards MAX_CONCURRENT streams in session
|
||||
uint32_t mCountAsActive : 1;
|
||||
|
||||
// Flag is set when a FIN has been placed on a data or header frame
|
||||
// (i.e after the client has closed)
|
||||
uint32_t mSentFin : 1;
|
||||
|
||||
// Flag is set after the WAITING_FOR Transport event has been generated
|
||||
uint32_t mSentWaitingFor : 1;
|
||||
|
||||
// Flag is set after TCP send autotuning has been disabled
|
||||
uint32_t mSetTCPSocketBuffer : 1;
|
||||
|
||||
// Flag is set when OnWriteSegment is being called directly from stream instead
|
||||
// of transaction
|
||||
uint32_t mBypassInputBuffer : 1;
|
||||
|
||||
// The InlineFrame and associated data is used for composing control
|
||||
// frames and data frame headers.
|
||||
UniquePtr<uint8_t[]> mTxInlineFrame;
|
||||
uint32_t mTxInlineFrameSize;
|
||||
uint32_t mTxInlineFrameUsed;
|
||||
|
||||
// mTxStreamFrameSize tracks the progress of
|
||||
// transmitting a request body data frame. The data frame itself
|
||||
// is never copied into the spdy layer.
|
||||
uint32_t mTxStreamFrameSize;
|
||||
|
||||
// Buffer for request header compression.
|
||||
nsCString mFlatHttpRequestHeaders;
|
||||
|
||||
// Track the content-length of a request body so that we can
|
||||
// place the fin flag on the last data packet instead of waiting
|
||||
// for a stream closed indication. Relying on stream close results
|
||||
// in an extra 0-length runt packet and seems to have some interop
|
||||
// problems with the google servers. Connect does rely on stream
|
||||
// close by setting this to the max value.
|
||||
int64_t mRequestBodyLenRemaining;
|
||||
|
||||
uint32_t mPriority; // geckoish weight
|
||||
uint32_t mPriorityDependency; // h2 stream id 3 - 0xb
|
||||
uint8_t mPriorityWeight; // h2 weight
|
||||
|
||||
// mClientReceiveWindow, mServerReceiveWindow, and mLocalUnacked are for flow control.
|
||||
// *window are signed because the race conditions in asynchronous SETTINGS
|
||||
// messages can force them temporarily negative.
|
||||
|
||||
// mClientReceiveWindow is how much data the server will send without getting a
|
||||
// window update
|
||||
int64_t mClientReceiveWindow;
|
||||
|
||||
// mServerReceiveWindow is how much data the client is allowed to send without
|
||||
// getting a window update
|
||||
int64_t mServerReceiveWindow;
|
||||
|
||||
// LocalUnacked is the number of bytes received by the client but not
|
||||
// yet reflected in a window update. Sending that update will increment
|
||||
// ClientReceiveWindow
|
||||
uint64_t mLocalUnacked;
|
||||
|
||||
// True when sending is suspended becuase the server receive window is
|
||||
// <= 0
|
||||
bool mBlockedOnRwin;
|
||||
|
||||
// For Progress Events
|
||||
uint64_t mTotalSent;
|
||||
uint64_t mTotalRead;
|
||||
|
||||
// For Http2Push
|
||||
Http2PushedStream *mPushSource;
|
||||
|
||||
// Used to store stream data when the transaction channel cannot keep up
|
||||
// and flow control has not yet kicked in.
|
||||
SimpleBuffer mSimpleBuffer;
|
||||
|
||||
/// connect tunnels
|
||||
public:
|
||||
bool IsTunnel() { return mIsTunnel; }
|
||||
private:
|
||||
void ClearTransactionsBlockedOnTunnel();
|
||||
void MapStreamToPlainText();
|
||||
void MapStreamToHttpConnection();
|
||||
|
||||
bool mIsTunnel;
|
||||
bool mPlainTextTunnel;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_Http2Stream_h
|
||||
3715
netwerk/protocol/http/HttpBaseChannel.cpp
Normal file
3715
netwerk/protocol/http/HttpBaseChannel.cpp
Normal file
File diff suppressed because it is too large
Load diff
660
netwerk/protocol/http/HttpBaseChannel.h
Normal file
660
netwerk/protocol/http/HttpBaseChannel.h
Normal file
|
|
@ -0,0 +1,660 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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_HttpBaseChannel_h
|
||||
#define mozilla_net_HttpBaseChannel_h
|
||||
|
||||
#include "nsHttp.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsHashPropertyBag.h"
|
||||
#include "nsProxyInfo.h"
|
||||
#include "nsHttpRequestHead.h"
|
||||
#include "nsHttpResponseHead.h"
|
||||
#include "nsHttpConnectionInfo.h"
|
||||
#include "nsIConsoleReportCollector.h"
|
||||
#include "nsIEncodedChannel.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsHttpHandler.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsIForcePendingChannel.h"
|
||||
#include "nsIFormPOSTActionChannel.h"
|
||||
#include "nsIUploadChannel2.h"
|
||||
#include "nsIProgressEventSink.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIEffectiveTLDService.h"
|
||||
#include "nsIStringEnumerator.h"
|
||||
#include "nsISupportsPriority.h"
|
||||
#include "nsIClassOfService.h"
|
||||
#include "nsIApplicationCache.h"
|
||||
#include "nsIResumableChannel.h"
|
||||
#include "nsITraceableChannel.h"
|
||||
#include "nsILoadContext.h"
|
||||
#include "nsILoadInfo.h"
|
||||
#include "mozilla/net/NeckoCommon.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "PrivateBrowsingChannel.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "nsITimedChannel.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsISecurityConsoleMessage.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "mozilla/net/ChannelEventQueue.h"
|
||||
#include "nsIThrottledInputChannel.h"
|
||||
|
||||
class nsISecurityConsoleMessage;
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class Performance;
|
||||
}
|
||||
|
||||
class LogCollector;
|
||||
|
||||
namespace net {
|
||||
extern mozilla::LazyLogModule gHttpLog;
|
||||
|
||||
/*
|
||||
* This class is a partial implementation of nsIHttpChannel. It contains code
|
||||
* shared by nsHttpChannel and HttpChannelChild.
|
||||
* - Note that this class has nothing to do with nsBaseChannel, which is an
|
||||
* earlier effort at a base class for channels that somehow never made it all
|
||||
* the way to the HTTP channel.
|
||||
*/
|
||||
class HttpBaseChannel : public nsHashPropertyBag
|
||||
, public nsIEncodedChannel
|
||||
, public nsIHttpChannel
|
||||
, public nsIHttpChannelInternal
|
||||
, public nsIFormPOSTActionChannel
|
||||
, public nsIUploadChannel2
|
||||
, public nsISupportsPriority
|
||||
, public nsIClassOfService
|
||||
, public nsIResumableChannel
|
||||
, public nsITraceableChannel
|
||||
, public PrivateBrowsingChannel<HttpBaseChannel>
|
||||
, public nsITimedChannel
|
||||
, public nsIForcePendingChannel
|
||||
, public nsIConsoleReportCollector
|
||||
, public nsIThrottledInputChannel
|
||||
{
|
||||
protected:
|
||||
virtual ~HttpBaseChannel();
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIUPLOADCHANNEL
|
||||
NS_DECL_NSIFORMPOSTACTIONCHANNEL
|
||||
NS_DECL_NSIUPLOADCHANNEL2
|
||||
NS_DECL_NSITRACEABLECHANNEL
|
||||
NS_DECL_NSITIMEDCHANNEL
|
||||
NS_DECL_NSITHROTTLEDINPUTCHANNEL
|
||||
|
||||
HttpBaseChannel();
|
||||
|
||||
virtual nsresult Init(nsIURI *aURI, uint32_t aCaps, nsProxyInfo *aProxyInfo,
|
||||
uint32_t aProxyResolveFlags,
|
||||
nsIURI *aProxyURI,
|
||||
const nsID& aChannelId);
|
||||
|
||||
// nsIRequest
|
||||
NS_IMETHOD GetName(nsACString& aName) override;
|
||||
NS_IMETHOD IsPending(bool *aIsPending) override;
|
||||
NS_IMETHOD GetStatus(nsresult *aStatus) override;
|
||||
NS_IMETHOD GetLoadGroup(nsILoadGroup **aLoadGroup) override;
|
||||
NS_IMETHOD SetLoadGroup(nsILoadGroup *aLoadGroup) override;
|
||||
NS_IMETHOD GetLoadFlags(nsLoadFlags *aLoadFlags) override;
|
||||
NS_IMETHOD SetLoadFlags(nsLoadFlags aLoadFlags) override;
|
||||
NS_IMETHOD SetDocshellUserAgentOverride();
|
||||
|
||||
// nsIChannel
|
||||
NS_IMETHOD GetOriginalURI(nsIURI **aOriginalURI) override;
|
||||
NS_IMETHOD SetOriginalURI(nsIURI *aOriginalURI) override;
|
||||
NS_IMETHOD GetURI(nsIURI **aURI) override;
|
||||
NS_IMETHOD GetOwner(nsISupports **aOwner) override;
|
||||
NS_IMETHOD SetOwner(nsISupports *aOwner) override;
|
||||
NS_IMETHOD GetLoadInfo(nsILoadInfo **aLoadInfo) override;
|
||||
NS_IMETHOD SetLoadInfo(nsILoadInfo *aLoadInfo) override;
|
||||
NS_IMETHOD GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks) override;
|
||||
NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks) override;
|
||||
NS_IMETHOD GetContentType(nsACString& aContentType) override;
|
||||
NS_IMETHOD SetContentType(const nsACString& aContentType) override;
|
||||
NS_IMETHOD GetContentCharset(nsACString& aContentCharset) override;
|
||||
NS_IMETHOD SetContentCharset(const nsACString& aContentCharset) override;
|
||||
NS_IMETHOD GetContentDisposition(uint32_t *aContentDisposition) override;
|
||||
NS_IMETHOD SetContentDisposition(uint32_t aContentDisposition) override;
|
||||
NS_IMETHOD GetContentDispositionFilename(nsAString& aContentDispositionFilename) override;
|
||||
NS_IMETHOD SetContentDispositionFilename(const nsAString& aContentDispositionFilename) override;
|
||||
NS_IMETHOD GetContentDispositionHeader(nsACString& aContentDispositionHeader) override;
|
||||
NS_IMETHOD GetContentLength(int64_t *aContentLength) override;
|
||||
NS_IMETHOD SetContentLength(int64_t aContentLength) override;
|
||||
NS_IMETHOD Open(nsIInputStream **aResult) override;
|
||||
NS_IMETHOD Open2(nsIInputStream **aResult) override;
|
||||
NS_IMETHOD GetBlockAuthPrompt(bool* aValue) override;
|
||||
NS_IMETHOD SetBlockAuthPrompt(bool aValue) override;
|
||||
|
||||
// nsIEncodedChannel
|
||||
NS_IMETHOD GetApplyConversion(bool *value) override;
|
||||
NS_IMETHOD SetApplyConversion(bool value) override;
|
||||
NS_IMETHOD GetContentEncodings(nsIUTF8StringEnumerator** aEncodings) override;
|
||||
NS_IMETHOD DoApplyContentConversions(nsIStreamListener *aNextListener,
|
||||
nsIStreamListener **aNewNextListener,
|
||||
nsISupports *aCtxt) override;
|
||||
|
||||
// HttpBaseChannel::nsIHttpChannel
|
||||
NS_IMETHOD GetRequestMethod(nsACString& aMethod) override;
|
||||
NS_IMETHOD SetRequestMethod(const nsACString& aMethod) override;
|
||||
NS_IMETHOD GetReferrer(nsIURI **referrer) override;
|
||||
NS_IMETHOD SetReferrer(nsIURI *referrer) override;
|
||||
NS_IMETHOD GetReferrerPolicy(uint32_t *referrerPolicy) override;
|
||||
NS_IMETHOD SetReferrerWithPolicy(nsIURI *referrer, uint32_t referrerPolicy) override;
|
||||
NS_IMETHOD GetRequestHeader(const nsACString& aHeader, nsACString& aValue) override;
|
||||
NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
|
||||
const nsACString& aValue, bool aMerge) override;
|
||||
NS_IMETHOD SetEmptyRequestHeader(const nsACString& aHeader) override;
|
||||
NS_IMETHOD VisitRequestHeaders(nsIHttpHeaderVisitor *visitor) override;
|
||||
NS_IMETHOD VisitNonDefaultRequestHeaders(nsIHttpHeaderVisitor *visitor) override;
|
||||
NS_IMETHOD GetResponseHeader(const nsACString &header, nsACString &value) override;
|
||||
NS_IMETHOD SetResponseHeader(const nsACString& header,
|
||||
const nsACString& value, bool merge) override;
|
||||
NS_IMETHOD VisitResponseHeaders(nsIHttpHeaderVisitor *visitor) override;
|
||||
NS_IMETHOD GetOriginalResponseHeader(const nsACString &aHeader,
|
||||
nsIHttpHeaderVisitor *aVisitor) override;
|
||||
NS_IMETHOD VisitOriginalResponseHeaders(nsIHttpHeaderVisitor *aVisitor) override;
|
||||
NS_IMETHOD GetAllowPipelining(bool *value) override;
|
||||
NS_IMETHOD SetAllowPipelining(bool value) override;
|
||||
NS_IMETHOD GetAllowSTS(bool *value) override;
|
||||
NS_IMETHOD SetAllowSTS(bool value) override;
|
||||
NS_IMETHOD GetRedirectionLimit(uint32_t *value) override;
|
||||
NS_IMETHOD SetRedirectionLimit(uint32_t value) override;
|
||||
NS_IMETHOD IsNoStoreResponse(bool *value) override;
|
||||
NS_IMETHOD IsNoCacheResponse(bool *value) override;
|
||||
NS_IMETHOD IsPrivateResponse(bool *value) override;
|
||||
NS_IMETHOD GetResponseStatus(uint32_t *aValue) override;
|
||||
NS_IMETHOD GetResponseStatusText(nsACString& aValue) override;
|
||||
NS_IMETHOD GetRequestSucceeded(bool *aValue) override;
|
||||
NS_IMETHOD RedirectTo(nsIURI *newURI) override;
|
||||
NS_IMETHOD GetRequestContextID(nsID *aRCID) override;
|
||||
NS_IMETHOD GetTransferSize(uint64_t *aTransferSize) override;
|
||||
NS_IMETHOD GetDecodedBodySize(uint64_t *aDecodedBodySize) override;
|
||||
NS_IMETHOD GetEncodedBodySize(uint64_t *aEncodedBodySize) override;
|
||||
NS_IMETHOD SetRequestContextID(const nsID aRCID) override;
|
||||
NS_IMETHOD GetIsMainDocumentChannel(bool* aValue) override;
|
||||
NS_IMETHOD SetIsMainDocumentChannel(bool aValue) override;
|
||||
NS_IMETHOD GetProtocolVersion(nsACString & aProtocolVersion) override;
|
||||
NS_IMETHOD GetChannelId(nsACString& aChannelId) override;
|
||||
NS_IMETHOD SetChannelId(const nsACString& aChannelId) override;
|
||||
NS_IMETHOD GetTopLevelContentWindowId(uint64_t *aContentWindowId) override;
|
||||
NS_IMETHOD SetTopLevelContentWindowId(uint64_t aContentWindowId) override;
|
||||
|
||||
// nsIHttpChannelInternal
|
||||
NS_IMETHOD GetDocumentURI(nsIURI **aDocumentURI) override;
|
||||
NS_IMETHOD SetDocumentURI(nsIURI *aDocumentURI) override;
|
||||
NS_IMETHOD GetRequestVersion(uint32_t *major, uint32_t *minor) override;
|
||||
NS_IMETHOD GetResponseVersion(uint32_t *major, uint32_t *minor) override;
|
||||
NS_IMETHOD SetCookie(const char *aCookieHeader) override;
|
||||
NS_IMETHOD GetThirdPartyFlags(uint32_t *aForce) override;
|
||||
NS_IMETHOD SetThirdPartyFlags(uint32_t aForce) override;
|
||||
NS_IMETHOD GetForceAllowThirdPartyCookie(bool *aForce) override;
|
||||
NS_IMETHOD SetForceAllowThirdPartyCookie(bool aForce) override;
|
||||
NS_IMETHOD GetCanceled(bool *aCanceled) override;
|
||||
NS_IMETHOD GetChannelIsForDownload(bool *aChannelIsForDownload) override;
|
||||
NS_IMETHOD SetChannelIsForDownload(bool aChannelIsForDownload) override;
|
||||
NS_IMETHOD SetCacheKeysRedirectChain(nsTArray<nsCString> *cacheKeys) override;
|
||||
NS_IMETHOD GetLocalAddress(nsACString& addr) override;
|
||||
NS_IMETHOD GetLocalPort(int32_t* port) override;
|
||||
NS_IMETHOD GetRemoteAddress(nsACString& addr) override;
|
||||
NS_IMETHOD GetRemotePort(int32_t* port) override;
|
||||
NS_IMETHOD GetAllowSpdy(bool *aAllowSpdy) override;
|
||||
NS_IMETHOD SetAllowSpdy(bool aAllowSpdy) override;
|
||||
NS_IMETHOD GetAllowAltSvc(bool *aAllowAltSvc) override;
|
||||
NS_IMETHOD SetAllowAltSvc(bool aAllowAltSvc) override;
|
||||
NS_IMETHOD GetBeConservative(bool *aBeConservative) override;
|
||||
NS_IMETHOD SetBeConservative(bool aBeConservative) override;
|
||||
NS_IMETHOD GetApiRedirectToURI(nsIURI * *aApiRedirectToURI) override;
|
||||
virtual nsresult AddSecurityMessage(const nsAString &aMessageTag, const nsAString &aMessageCategory);
|
||||
NS_IMETHOD TakeAllSecurityMessages(nsCOMArray<nsISecurityConsoleMessage> &aMessages) override;
|
||||
NS_IMETHOD GetResponseTimeoutEnabled(bool *aEnable) override;
|
||||
NS_IMETHOD SetResponseTimeoutEnabled(bool aEnable) override;
|
||||
NS_IMETHOD GetInitialRwin(uint32_t* aRwin) override;
|
||||
NS_IMETHOD SetInitialRwin(uint32_t aRwin) override;
|
||||
NS_IMETHOD GetNetworkInterfaceId(nsACString& aNetworkInterfaceId) override;
|
||||
NS_IMETHOD SetNetworkInterfaceId(const nsACString& aNetworkInterfaceId) override;
|
||||
NS_IMETHOD ForcePending(bool aForcePending) override;
|
||||
NS_IMETHOD GetLastModifiedTime(PRTime* lastModifiedTime) override;
|
||||
NS_IMETHOD GetCorsIncludeCredentials(bool* aInclude) override;
|
||||
NS_IMETHOD SetCorsIncludeCredentials(bool aInclude) override;
|
||||
NS_IMETHOD GetCorsMode(uint32_t* aCorsMode) override;
|
||||
NS_IMETHOD SetCorsMode(uint32_t aCorsMode) override;
|
||||
NS_IMETHOD GetRedirectMode(uint32_t* aRedirectMode) override;
|
||||
NS_IMETHOD SetRedirectMode(uint32_t aRedirectMode) override;
|
||||
NS_IMETHOD GetFetchCacheMode(uint32_t* aFetchCacheMode) override;
|
||||
NS_IMETHOD SetFetchCacheMode(uint32_t aFetchCacheMode) override;
|
||||
NS_IMETHOD GetTopWindowURI(nsIURI **aTopWindowURI) override;
|
||||
NS_IMETHOD GetProxyURI(nsIURI **proxyURI) override;
|
||||
virtual void SetCorsPreflightParameters(const nsTArray<nsCString>& unsafeHeaders) override;
|
||||
NS_IMETHOD GetConnectionInfoHashKey(nsACString& aConnectionInfoHashKey) override;
|
||||
NS_IMETHOD GetIntegrityMetadata(nsAString& aIntegrityMetadata) override;
|
||||
NS_IMETHOD SetIntegrityMetadata(const nsAString& aIntegrityMetadata) override;
|
||||
virtual mozilla::net::nsHttpChannel * QueryHttpChannelImpl(void) override;
|
||||
|
||||
inline void CleanRedirectCacheChainIfNecessary()
|
||||
{
|
||||
mRedirectedCachekeys = nullptr;
|
||||
}
|
||||
NS_IMETHOD HTTPUpgrade(const nsACString & aProtocolName,
|
||||
nsIHttpUpgradeListener *aListener) override;
|
||||
|
||||
// nsISupportsPriority
|
||||
NS_IMETHOD GetPriority(int32_t *value) override;
|
||||
NS_IMETHOD AdjustPriority(int32_t delta) override;
|
||||
|
||||
// nsIClassOfService
|
||||
NS_IMETHOD GetClassFlags(uint32_t *outFlags) override { *outFlags = mClassOfService; return NS_OK; }
|
||||
|
||||
// nsIResumableChannel
|
||||
NS_IMETHOD GetEntityID(nsACString& aEntityID) override;
|
||||
|
||||
|
||||
// nsIConsoleReportCollector
|
||||
void
|
||||
AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory,
|
||||
nsContentUtils::PropertiesFile aPropertiesFile,
|
||||
const nsACString& aSourceFileURI,
|
||||
uint32_t aLineNumber, uint32_t aColumnNumber,
|
||||
const nsACString& aMessageName,
|
||||
const nsTArray<nsString>& aStringParams) override;
|
||||
|
||||
void
|
||||
FlushConsoleReports(nsIDocument* aDocument,
|
||||
ReportAction aAction = ReportAction::Forget) override;
|
||||
|
||||
void
|
||||
FlushConsoleReports(nsIConsoleReportCollector* aCollector) override;
|
||||
|
||||
void
|
||||
FlushReportsByWindowId(uint64_t aWindowId,
|
||||
ReportAction aAction = ReportAction::Forget) override;
|
||||
|
||||
void
|
||||
ClearConsoleReports() override;
|
||||
|
||||
class nsContentEncodings : public nsIUTF8StringEnumerator
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIUTF8STRINGENUMERATOR
|
||||
|
||||
nsContentEncodings(nsIHttpChannel* aChannel, const char* aEncodingHeader);
|
||||
|
||||
private:
|
||||
virtual ~nsContentEncodings();
|
||||
|
||||
nsresult PrepareForNext(void);
|
||||
|
||||
// We do not own the buffer. The channel owns it.
|
||||
const char* mEncodingHeader;
|
||||
const char* mCurStart; // points to start of current header
|
||||
const char* mCurEnd; // points to end of current header
|
||||
|
||||
// Hold a ref to our channel so that it can't go away and take the
|
||||
// header with it.
|
||||
nsCOMPtr<nsIHttpChannel> mChannel;
|
||||
|
||||
bool mReady;
|
||||
};
|
||||
|
||||
nsHttpResponseHead * GetResponseHead() const { return mResponseHead; }
|
||||
nsHttpRequestHead * GetRequestHead() { return &mRequestHead; }
|
||||
|
||||
const NetAddr& GetSelfAddr() { return mSelfAddr; }
|
||||
const NetAddr& GetPeerAddr() { return mPeerAddr; }
|
||||
|
||||
nsresult OverrideSecurityInfo(nsISupports* aSecurityInfo);
|
||||
|
||||
public: /* Necko internal use only... */
|
||||
bool IsNavigation();
|
||||
|
||||
// Return whether upon a redirect code of httpStatus for method, the
|
||||
// request method should be rewritten to GET.
|
||||
static bool ShouldRewriteRedirectToGET(uint32_t httpStatus,
|
||||
nsHttpRequestHead::ParsedMethodType method);
|
||||
|
||||
// Like nsIEncodedChannel::DoApplyConversions except context is set to
|
||||
// mListenerContext.
|
||||
nsresult DoApplyContentConversions(nsIStreamListener *aNextListener,
|
||||
nsIStreamListener **aNewNextListener);
|
||||
|
||||
// Callback on main thread when NS_AsyncCopy() is finished populating
|
||||
// the new mUploadStream.
|
||||
void EnsureUploadStreamIsCloneableComplete(nsresult aStatus);
|
||||
|
||||
protected:
|
||||
nsCOMArray<nsISecurityConsoleMessage> mSecurityConsoleMessages;
|
||||
|
||||
// Handle notifying listener, removing from loadgroup if request failed.
|
||||
void DoNotifyListener();
|
||||
virtual void DoNotifyListenerCleanup() = 0;
|
||||
|
||||
// drop reference to listener, its callbacks, and the progress sink
|
||||
void ReleaseListeners();
|
||||
|
||||
// This is fired only when a cookie is created due to the presence of
|
||||
// Set-Cookie header in the response header of any network request.
|
||||
// This notification will come only after the "http-on-examine-response"
|
||||
// was fired.
|
||||
void NotifySetCookie(char const *aCookie);
|
||||
|
||||
mozilla::dom::Performance* GetPerformance();
|
||||
nsIURI* GetReferringPage();
|
||||
nsPIDOMWindowInner* GetInnerDOMWindow();
|
||||
|
||||
void AddCookiesToRequest();
|
||||
virtual nsresult SetupReplacementChannel(nsIURI *,
|
||||
nsIChannel *,
|
||||
bool preserveMethod,
|
||||
uint32_t redirectFlags);
|
||||
|
||||
// bundle calling OMR observers and marking flag into one function
|
||||
inline void CallOnModifyRequestObservers() {
|
||||
gHttpHandler->OnModifyRequest(this);
|
||||
mRequestObserversCalled = true;
|
||||
}
|
||||
|
||||
// Helper function to simplify getting notification callbacks.
|
||||
template <class T>
|
||||
void GetCallback(nsCOMPtr<T> &aResult)
|
||||
{
|
||||
NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
|
||||
NS_GET_TEMPLATE_IID(T),
|
||||
getter_AddRefs(aResult));
|
||||
}
|
||||
|
||||
// Redirect tracking
|
||||
// Checks whether or not aURI and mOriginalURI share the same domain.
|
||||
bool SameOriginWithOriginalUri(nsIURI *aURI);
|
||||
|
||||
// GetPrincipal Returns the channel's URI principal.
|
||||
nsIPrincipal *GetURIPrincipal();
|
||||
|
||||
bool BypassServiceWorker() const;
|
||||
|
||||
// Returns true if this channel should intercept the network request and prepare
|
||||
// for a possible synthesized response instead.
|
||||
bool ShouldIntercept(nsIURI* aURI = nullptr);
|
||||
|
||||
#ifdef DEBUG
|
||||
// Check if mPrivateBrowsingId matches between LoadInfo and LoadContext.
|
||||
void AssertPrivateBrowsingId();
|
||||
#endif
|
||||
|
||||
friend class PrivateBrowsingChannel<HttpBaseChannel>;
|
||||
friend class InterceptFailedOnStop;
|
||||
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
nsCOMPtr<nsIURI> mOriginalURI;
|
||||
nsCOMPtr<nsIURI> mDocumentURI;
|
||||
nsCOMPtr<nsIStreamListener> mListener;
|
||||
nsCOMPtr<nsISupports> mListenerContext;
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
nsCOMPtr<nsILoadInfo> mLoadInfo;
|
||||
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
|
||||
nsCOMPtr<nsIProgressEventSink> mProgressSink;
|
||||
nsCOMPtr<nsIURI> mReferrer;
|
||||
nsCOMPtr<nsIApplicationCache> mApplicationCache;
|
||||
|
||||
// An instance of nsHTTPCompressConv
|
||||
nsCOMPtr<nsIStreamListener> mCompressListener;
|
||||
|
||||
nsHttpRequestHead mRequestHead;
|
||||
// Upload throttling.
|
||||
nsCOMPtr<nsIInputChannelThrottleQueue> mThrottleQueue;
|
||||
nsCOMPtr<nsIInputStream> mUploadStream;
|
||||
nsCOMPtr<nsIRunnable> mUploadCloneableCallback;
|
||||
nsAutoPtr<nsHttpResponseHead> mResponseHead;
|
||||
RefPtr<nsHttpConnectionInfo> mConnectionInfo;
|
||||
nsCOMPtr<nsIProxyInfo> mProxyInfo;
|
||||
nsCOMPtr<nsISupports> mSecurityInfo;
|
||||
|
||||
nsCString mSpec; // ASCII encoded URL spec
|
||||
nsCString mContentTypeHint;
|
||||
nsCString mContentCharsetHint;
|
||||
nsCString mUserSetCookieHeader;
|
||||
|
||||
NetAddr mSelfAddr;
|
||||
NetAddr mPeerAddr;
|
||||
|
||||
// HTTP Upgrade Data
|
||||
nsCString mUpgradeProtocol;
|
||||
nsCOMPtr<nsIHttpUpgradeListener> mUpgradeProtocolCallback;
|
||||
|
||||
// Resumable channel specific data
|
||||
nsCString mEntityID;
|
||||
uint64_t mStartPos;
|
||||
|
||||
nsresult mStatus;
|
||||
uint32_t mLoadFlags;
|
||||
uint32_t mCaps;
|
||||
uint32_t mClassOfService;
|
||||
int16_t mPriority;
|
||||
uint8_t mRedirectionLimit;
|
||||
|
||||
uint32_t mApplyConversion : 1;
|
||||
uint32_t mCanceled : 1;
|
||||
uint32_t mIsPending : 1;
|
||||
uint32_t mWasOpened : 1;
|
||||
// if 1 all "http-on-{opening|modify|etc}-request" observers have been called
|
||||
uint32_t mRequestObserversCalled : 1;
|
||||
uint32_t mResponseHeadersModified : 1;
|
||||
uint32_t mAllowPipelining : 1;
|
||||
uint32_t mAllowSTS : 1;
|
||||
uint32_t mThirdPartyFlags : 3;
|
||||
uint32_t mUploadStreamHasHeaders : 1;
|
||||
uint32_t mInheritApplicationCache : 1;
|
||||
uint32_t mChooseApplicationCache : 1;
|
||||
uint32_t mLoadedFromApplicationCache : 1;
|
||||
uint32_t mChannelIsForDownload : 1;
|
||||
uint32_t mTracingEnabled : 1;
|
||||
// True if timing collection is enabled
|
||||
uint32_t mTimingEnabled : 1;
|
||||
uint32_t mAllowSpdy : 1;
|
||||
uint32_t mAllowAltSvc : 1;
|
||||
uint32_t mBeConservative : 1;
|
||||
uint32_t mResponseTimeoutEnabled : 1;
|
||||
// A flag that should be false only if a cross-domain redirect occurred
|
||||
uint32_t mAllRedirectsSameOrigin : 1;
|
||||
|
||||
// Is 1 if no redirects have occured or if all redirects
|
||||
// pass the Resource Timing timing-allow-check
|
||||
uint32_t mAllRedirectsPassTimingAllowCheck : 1;
|
||||
|
||||
// True if this channel was intercepted and could receive a synthesized response.
|
||||
uint32_t mResponseCouldBeSynthesized : 1;
|
||||
|
||||
uint32_t mBlockAuthPrompt : 1;
|
||||
|
||||
// If true, we behave as if the LOAD_FROM_CACHE flag has been set.
|
||||
// Used to enforce that flag's behavior but not expose it externally.
|
||||
uint32_t mAllowStaleCacheContent : 1;
|
||||
|
||||
// Current suspension depth for this channel object
|
||||
uint32_t mSuspendCount;
|
||||
|
||||
// Per channel transport window override (0 means no override)
|
||||
uint32_t mInitialRwin;
|
||||
|
||||
nsCOMPtr<nsIURI> mAPIRedirectToURI;
|
||||
nsAutoPtr<nsTArray<nsCString> > mRedirectedCachekeys;
|
||||
|
||||
uint32_t mProxyResolveFlags;
|
||||
nsCOMPtr<nsIURI> mProxyURI;
|
||||
|
||||
uint32_t mContentDispositionHint;
|
||||
nsAutoPtr<nsString> mContentDispositionFilename;
|
||||
|
||||
RefPtr<nsHttpHandler> mHttpHandler; // keep gHttpHandler alive
|
||||
|
||||
uint32_t mReferrerPolicy;
|
||||
|
||||
// Performance tracking
|
||||
// The initiator type (for this resource) - how was the resource referenced in
|
||||
// the HTML file.
|
||||
nsString mInitiatorType;
|
||||
// Number of redirects that has occurred.
|
||||
int16_t mRedirectCount;
|
||||
// A time value equal to the starting time of the fetch that initiates the
|
||||
// redirect.
|
||||
mozilla::TimeStamp mRedirectStartTimeStamp;
|
||||
// A time value equal to the time immediately after receiving the last byte of
|
||||
// the response of the last redirect.
|
||||
mozilla::TimeStamp mRedirectEndTimeStamp;
|
||||
|
||||
PRTime mChannelCreationTime;
|
||||
TimeStamp mChannelCreationTimestamp;
|
||||
TimeStamp mAsyncOpenTime;
|
||||
TimeStamp mCacheReadStart;
|
||||
TimeStamp mCacheReadEnd;
|
||||
// copied from the transaction before we null out mTransaction
|
||||
// so that the timing can still be queried from OnStopRequest
|
||||
TimingStruct mTransactionTimings;
|
||||
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
bool mForcePending;
|
||||
nsCOMPtr<nsIURI> mTopWindowURI;
|
||||
|
||||
bool mCorsIncludeCredentials;
|
||||
uint32_t mCorsMode;
|
||||
uint32_t mRedirectMode;
|
||||
uint32_t mFetchCacheMode;
|
||||
|
||||
// These parameters are used to ensure that we do not call OnStartRequest and
|
||||
// OnStopRequest more than once.
|
||||
bool mOnStartRequestCalled;
|
||||
bool mOnStopRequestCalled;
|
||||
|
||||
// Defaults to false. Is set to true at the begining of OnStartRequest.
|
||||
// Used to ensure methods can't be called before OnStartRequest.
|
||||
bool mAfterOnStartRequestBegun;
|
||||
|
||||
uint64_t mTransferSize;
|
||||
uint64_t mDecodedBodySize;
|
||||
uint64_t mEncodedBodySize;
|
||||
|
||||
// The network interface id that's associated with this channel.
|
||||
nsCString mNetworkInterfaceId;
|
||||
|
||||
nsID mRequestContextID;
|
||||
bool EnsureRequestContextID();
|
||||
|
||||
// ID of the top-level document's inner window this channel is being
|
||||
// originated from.
|
||||
uint64_t mContentWindowId;
|
||||
|
||||
bool mRequireCORSPreflight;
|
||||
nsTArray<nsCString> mUnsafeHeaders;
|
||||
|
||||
nsCOMPtr<nsIConsoleReportCollector> mReportCollector;
|
||||
|
||||
// Holds the name of the preferred alt-data type.
|
||||
nsCString mPreferredCachedAltDataType;
|
||||
// Holds the name of the alternative data type the channel returned.
|
||||
nsCString mAvailableCachedAltDataType;
|
||||
|
||||
bool mForceMainDocumentChannel;
|
||||
|
||||
nsID mChannelId;
|
||||
|
||||
nsString mIntegrityMetadata;
|
||||
};
|
||||
|
||||
// Share some code while working around C++'s absurd inability to handle casting
|
||||
// of member functions between base/derived types.
|
||||
// - We want to store member function pointer to call at resume time, but one
|
||||
// such function--HandleAsyncAbort--we want to share between the
|
||||
// nsHttpChannel/HttpChannelChild. Can't define it in base class, because
|
||||
// then we'd have to cast member function ptr between base/derived class
|
||||
// types. Sigh...
|
||||
template <class T>
|
||||
class HttpAsyncAborter
|
||||
{
|
||||
public:
|
||||
explicit HttpAsyncAborter(T *derived) : mThis(derived), mCallOnResume(0) {}
|
||||
|
||||
// Aborts channel: calls OnStart/Stop with provided status, removes channel
|
||||
// from loadGroup.
|
||||
nsresult AsyncAbort(nsresult status);
|
||||
|
||||
// Does most the actual work.
|
||||
void HandleAsyncAbort();
|
||||
|
||||
// AsyncCall calls a member function asynchronously (via an event).
|
||||
// retval isn't refcounted and is set only when event was successfully
|
||||
// posted, the event is returned for the purpose of cancelling when needed
|
||||
nsresult AsyncCall(void (T::*funcPtr)(),
|
||||
nsRunnableMethod<T> **retval = nullptr);
|
||||
private:
|
||||
T *mThis;
|
||||
|
||||
protected:
|
||||
// Function to be called at resume time
|
||||
void (T::* mCallOnResume)(void);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
nsresult HttpAsyncAborter<T>::AsyncAbort(nsresult status)
|
||||
{
|
||||
MOZ_LOG(gHttpLog, LogLevel::Debug,
|
||||
("HttpAsyncAborter::AsyncAbort [this=%p status=%x]\n", mThis, status));
|
||||
|
||||
mThis->mStatus = status;
|
||||
|
||||
// if this fails? Callers ignore our return value anyway....
|
||||
return AsyncCall(&T::HandleAsyncAbort);
|
||||
}
|
||||
|
||||
// Each subclass needs to define its own version of this (which just calls this
|
||||
// base version), else we wind up casting base/derived member function ptrs
|
||||
template <class T>
|
||||
inline void HttpAsyncAborter<T>::HandleAsyncAbort()
|
||||
{
|
||||
NS_PRECONDITION(!mCallOnResume, "How did that happen?");
|
||||
|
||||
if (mThis->mSuspendCount) {
|
||||
MOZ_LOG(gHttpLog, LogLevel::Debug,
|
||||
("Waiting until resume to do async notification [this=%p]\n", mThis));
|
||||
mCallOnResume = &T::HandleAsyncAbort;
|
||||
return;
|
||||
}
|
||||
|
||||
mThis->DoNotifyListener();
|
||||
|
||||
// finally remove ourselves from the load group.
|
||||
if (mThis->mLoadGroup)
|
||||
mThis->mLoadGroup->RemoveRequest(mThis, nullptr, mThis->mStatus);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
nsresult HttpAsyncAborter<T>::AsyncCall(void (T::*funcPtr)(),
|
||||
nsRunnableMethod<T> **retval)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
RefPtr<nsRunnableMethod<T>> event = NewRunnableMethod(mThis, funcPtr);
|
||||
rv = NS_DispatchToCurrentThread(event);
|
||||
if (NS_SUCCEEDED(rv) && retval) {
|
||||
*retval = event;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_HttpBaseChannel_h
|
||||
2849
netwerk/protocol/http/HttpChannelChild.cpp
Normal file
2849
netwerk/protocol/http/HttpChannelChild.cpp
Normal file
File diff suppressed because it is too large
Load diff
390
netwerk/protocol/http/HttpChannelChild.h
Normal file
390
netwerk/protocol/http/HttpChannelChild.h
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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_HttpChannelChild_h
|
||||
#define mozilla_net_HttpChannelChild_h
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/net/HttpBaseChannel.h"
|
||||
#include "mozilla/net/PHttpChannelChild.h"
|
||||
#include "mozilla/net/ChannelEventQueue.h"
|
||||
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsILoadGroup.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIProgressEventSink.h"
|
||||
#include "nsICacheInfoChannel.h"
|
||||
#include "nsIApplicationCache.h"
|
||||
#include "nsIApplicationCacheChannel.h"
|
||||
#include "nsIUploadChannel2.h"
|
||||
#include "nsIResumableChannel.h"
|
||||
#include "nsIProxiedChannel.h"
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "nsIAssociatedContentSecurity.h"
|
||||
#include "nsIChildChannel.h"
|
||||
#include "nsIHttpChannelChild.h"
|
||||
#include "nsIDivertableChannel.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
|
||||
class nsInputStreamPump;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class InterceptedChannelContent;
|
||||
class InterceptStreamListener;
|
||||
|
||||
class HttpChannelChild final : public PHttpChannelChild
|
||||
, public HttpBaseChannel
|
||||
, public HttpAsyncAborter<HttpChannelChild>
|
||||
, public nsICacheInfoChannel
|
||||
, public nsIProxiedChannel
|
||||
, public nsIApplicationCacheChannel
|
||||
, public nsIAsyncVerifyRedirectCallback
|
||||
, public nsIAssociatedContentSecurity
|
||||
, public nsIChildChannel
|
||||
, public nsIHttpChannelChild
|
||||
, public nsIDivertableChannel
|
||||
{
|
||||
virtual ~HttpChannelChild();
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSICACHEINFOCHANNEL
|
||||
NS_DECL_NSIPROXIEDCHANNEL
|
||||
NS_DECL_NSIAPPLICATIONCACHECONTAINER
|
||||
NS_DECL_NSIAPPLICATIONCACHECHANNEL
|
||||
NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
|
||||
NS_DECL_NSIASSOCIATEDCONTENTSECURITY
|
||||
NS_DECL_NSICHILDCHANNEL
|
||||
NS_DECL_NSIHTTPCHANNELCHILD
|
||||
NS_DECL_NSIDIVERTABLECHANNEL
|
||||
|
||||
HttpChannelChild();
|
||||
|
||||
// Methods HttpBaseChannel didn't implement for us or that we override.
|
||||
//
|
||||
// nsIRequest
|
||||
NS_IMETHOD Cancel(nsresult status) override;
|
||||
NS_IMETHOD Suspend() override;
|
||||
NS_IMETHOD Resume() override;
|
||||
// nsIChannel
|
||||
NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo) override;
|
||||
NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) override;
|
||||
NS_IMETHOD AsyncOpen2(nsIStreamListener *aListener) override;
|
||||
|
||||
// HttpBaseChannel::nsIHttpChannel
|
||||
NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
|
||||
const nsACString& aValue,
|
||||
bool aMerge) override;
|
||||
NS_IMETHOD SetEmptyRequestHeader(const nsACString& aHeader) override;
|
||||
NS_IMETHOD RedirectTo(nsIURI *newURI) override;
|
||||
NS_IMETHOD GetProtocolVersion(nsACString& aProtocolVersion) override;
|
||||
// nsIHttpChannelInternal
|
||||
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
|
||||
NS_IMETHOD ForceIntercepted(uint64_t aInterceptionID) override;
|
||||
// nsISupportsPriority
|
||||
NS_IMETHOD SetPriority(int32_t value) override;
|
||||
// nsIClassOfService
|
||||
NS_IMETHOD SetClassFlags(uint32_t inFlags) override;
|
||||
NS_IMETHOD AddClassFlags(uint32_t inFlags) override;
|
||||
NS_IMETHOD ClearClassFlags(uint32_t inFlags) override;
|
||||
// nsIResumableChannel
|
||||
NS_IMETHOD ResumeAt(uint64_t startPos, const nsACString& entityID) override;
|
||||
|
||||
// IPDL holds a reference while the PHttpChannel protocol is live (starting at
|
||||
// AsyncOpen, and ending at either OnStopRequest or any IPDL error, either of
|
||||
// which call NeckoChild::DeallocPHttpChannelChild()).
|
||||
void AddIPDLReference();
|
||||
void ReleaseIPDLReference();
|
||||
|
||||
bool IsSuspended();
|
||||
|
||||
bool RecvNotifyTrackingProtectionDisabled() override;
|
||||
void FlushedForDiversion();
|
||||
|
||||
protected:
|
||||
bool RecvOnStartRequest(const nsresult& channelStatus,
|
||||
const nsHttpResponseHead& responseHead,
|
||||
const bool& useResponseHead,
|
||||
const nsHttpHeaderArray& requestHeaders,
|
||||
const bool& isFromCache,
|
||||
const bool& cacheEntryAvailable,
|
||||
const uint32_t& cacheExpirationTime,
|
||||
const nsCString& cachedCharset,
|
||||
const nsCString& securityInfoSerialization,
|
||||
const NetAddr& selfAddr,
|
||||
const NetAddr& peerAddr,
|
||||
const int16_t& redirectCount,
|
||||
const uint32_t& cacheKey,
|
||||
const nsCString& altDataType) override;
|
||||
bool RecvOnTransportAndData(const nsresult& channelStatus,
|
||||
const nsresult& status,
|
||||
const uint64_t& progress,
|
||||
const uint64_t& progressMax,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count,
|
||||
const nsCString& data) override;
|
||||
bool RecvOnStopRequest(const nsresult& statusCode, const ResourceTimingStruct& timing) override;
|
||||
bool RecvOnProgress(const int64_t& progress, const int64_t& progressMax) override;
|
||||
bool RecvOnStatus(const nsresult& status) override;
|
||||
bool RecvFailedAsyncOpen(const nsresult& status) override;
|
||||
bool RecvRedirect1Begin(const uint32_t& registrarId,
|
||||
const URIParams& newURI,
|
||||
const uint32_t& redirectFlags,
|
||||
const nsHttpResponseHead& responseHead,
|
||||
const nsCString& securityInfoSerialization,
|
||||
const nsCString& channelId) override;
|
||||
bool RecvRedirect3Complete() override;
|
||||
bool RecvAssociateApplicationCache(const nsCString& groupID,
|
||||
const nsCString& clientID) override;
|
||||
bool RecvFlushedForDiversion() override;
|
||||
bool RecvDivertMessages() override;
|
||||
bool RecvDeleteSelf() override;
|
||||
bool RecvFinishInterceptedRedirect() override;
|
||||
|
||||
bool RecvReportSecurityMessage(const nsString& messageTag,
|
||||
const nsString& messageCategory) override;
|
||||
|
||||
bool RecvIssueDeprecationWarning(const uint32_t& warning,
|
||||
const bool& asError) override;
|
||||
|
||||
bool GetAssociatedContentSecurity(nsIAssociatedContentSecurity** res = nullptr);
|
||||
virtual void DoNotifyListenerCleanup() override;
|
||||
|
||||
NS_IMETHOD GetResponseSynthesized(bool* aSynthesized) override;
|
||||
|
||||
private:
|
||||
|
||||
class OverrideRunnable : public Runnable {
|
||||
public:
|
||||
OverrideRunnable(HttpChannelChild* aChannel,
|
||||
HttpChannelChild* aNewChannel,
|
||||
InterceptStreamListener* aListener,
|
||||
nsIInputStream* aInput,
|
||||
nsAutoPtr<nsHttpResponseHead>& aHead);
|
||||
|
||||
NS_IMETHOD Run() override;
|
||||
void OverrideWithSynthesizedResponse();
|
||||
private:
|
||||
RefPtr<HttpChannelChild> mChannel;
|
||||
RefPtr<HttpChannelChild> mNewChannel;
|
||||
RefPtr<InterceptStreamListener> mListener;
|
||||
nsCOMPtr<nsIInputStream> mInput;
|
||||
nsAutoPtr<nsHttpResponseHead> mHead;
|
||||
};
|
||||
|
||||
nsresult ContinueAsyncOpen();
|
||||
|
||||
void DoOnStartRequest(nsIRequest* aRequest, nsISupports* aContext);
|
||||
void DoOnStatus(nsIRequest* aRequest, nsresult status);
|
||||
void DoOnProgress(nsIRequest* aRequest, int64_t progress, int64_t progressMax);
|
||||
void DoOnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, nsIInputStream* aStream,
|
||||
uint64_t offset, uint32_t count);
|
||||
void DoPreOnStopRequest(nsresult aStatus);
|
||||
void DoOnStopRequest(nsIRequest* aRequest, nsresult aChannelStatus, nsISupports* aContext);
|
||||
|
||||
bool ShouldInterceptURI(nsIURI* aURI, bool& aShouldUpgrade);
|
||||
|
||||
// Discard the prior interception and continue with the original network request.
|
||||
void ResetInterception();
|
||||
|
||||
// Override this channel's pending response with a synthesized one. The content will be
|
||||
// asynchronously read from the pump.
|
||||
void OverrideWithSynthesizedResponse(nsAutoPtr<nsHttpResponseHead>& aResponseHead,
|
||||
nsIInputStream* aSynthesizedInput,
|
||||
InterceptStreamListener* aStreamListener);
|
||||
|
||||
void ForceIntercepted(nsIInputStream* aSynthesizedInput);
|
||||
|
||||
RequestHeaderTuples mClientSetRequestHeaders;
|
||||
nsCOMPtr<nsIChildChannel> mRedirectChannelChild;
|
||||
RefPtr<InterceptStreamListener> mInterceptListener;
|
||||
RefPtr<nsInputStreamPump> mSynthesizedResponsePump;
|
||||
nsCOMPtr<nsIInputStream> mSynthesizedInput;
|
||||
int64_t mSynthesizedStreamLength;
|
||||
|
||||
bool mIsFromCache;
|
||||
bool mCacheEntryAvailable;
|
||||
uint32_t mCacheExpirationTime;
|
||||
nsCString mCachedCharset;
|
||||
nsCOMPtr<nsISupports> mCacheKey;
|
||||
|
||||
nsCString mProtocolVersion;
|
||||
|
||||
// If ResumeAt is called before AsyncOpen, we need to send extra data upstream
|
||||
bool mSendResumeAt;
|
||||
|
||||
bool mIPCOpen;
|
||||
bool mKeptAlive; // IPC kept open, but only for security info
|
||||
RefPtr<ChannelEventQueue> mEventQ;
|
||||
|
||||
// If nsUnknownDecoder is involved OnStartRequest call will be delayed and
|
||||
// this queue keeps OnDataAvailable data until OnStartRequest is finally
|
||||
// called.
|
||||
nsTArray<UniquePtr<ChannelEvent>> mUnknownDecoderEventQ;
|
||||
bool mUnknownDecoderInvolved;
|
||||
|
||||
// Once set, OnData and possibly OnStop will be diverted to the parent.
|
||||
bool mDivertingToParent;
|
||||
// Once set, no OnStart/OnData/OnStop callbacks should be received from the
|
||||
// parent channel, nor dequeued from the ChannelEventQueue.
|
||||
bool mFlushedForDiversion;
|
||||
// Set if SendSuspend is called. Determines if SendResume is needed when
|
||||
// diverting callbacks to parent.
|
||||
bool mSuspendSent;
|
||||
|
||||
// Set if a response was synthesized, indicating that any forthcoming redirects
|
||||
// should be intercepted.
|
||||
bool mSynthesizedResponse;
|
||||
|
||||
// Set if a synthesized response should cause us to explictly allows intercepting
|
||||
// an expected forthcoming redirect.
|
||||
bool mShouldInterceptSubsequentRedirect;
|
||||
// Set if a redirection is being initiated to facilitate providing a synthesized
|
||||
// response to a channel using a different principal than the current one.
|
||||
bool mRedirectingForSubsequentSynthesizedResponse;
|
||||
|
||||
// Set if a manual redirect mode channel needs to be intercepted in the
|
||||
// parent.
|
||||
bool mPostRedirectChannelShouldIntercept;
|
||||
// Set if a manual redirect mode channel needs to be upgraded to a secure URI
|
||||
// when it's being considered for interception. Can only be true if
|
||||
// mPostRedirectChannelShouldIntercept is true.
|
||||
bool mPostRedirectChannelShouldUpgrade;
|
||||
|
||||
// Set if the corresponding parent channel should force an interception to occur
|
||||
// before the network transaction is initiated.
|
||||
bool mShouldParentIntercept;
|
||||
|
||||
// Set if the corresponding parent channel should suspend after a response
|
||||
// is synthesized.
|
||||
bool mSuspendParentAfterSynthesizeResponse;
|
||||
|
||||
// Needed to call AsyncOpen in FinishInterceptedRedirect
|
||||
nsCOMPtr<nsIStreamListener> mInterceptedRedirectListener;
|
||||
nsCOMPtr<nsISupports> mInterceptedRedirectContext;
|
||||
// Needed to call CleanupRedirectingChannel in FinishInterceptedRedirect
|
||||
RefPtr<HttpChannelChild> mInterceptingChannel;
|
||||
// Used to call OverrideWithSynthesizedResponse in FinishInterceptedRedirect
|
||||
RefPtr<OverrideRunnable> mOverrideRunnable;
|
||||
|
||||
void FinishInterceptedRedirect();
|
||||
void CleanupRedirectingChannel(nsresult rv);
|
||||
|
||||
// true after successful AsyncOpen until OnStopRequest completes.
|
||||
bool RemoteChannelExists() { return mIPCOpen && !mKeptAlive; }
|
||||
|
||||
void AssociateApplicationCache(const nsCString &groupID,
|
||||
const nsCString &clientID);
|
||||
void OnStartRequest(const nsresult& channelStatus,
|
||||
const nsHttpResponseHead& responseHead,
|
||||
const bool& useResponseHead,
|
||||
const nsHttpHeaderArray& requestHeaders,
|
||||
const bool& isFromCache,
|
||||
const bool& cacheEntryAvailable,
|
||||
const uint32_t& cacheExpirationTime,
|
||||
const nsCString& cachedCharset,
|
||||
const nsCString& securityInfoSerialization,
|
||||
const NetAddr& selfAddr,
|
||||
const NetAddr& peerAddr,
|
||||
const uint32_t& cacheKey,
|
||||
const nsCString& altDataType);
|
||||
void MaybeDivertOnData(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count);
|
||||
void OnTransportAndData(const nsresult& channelStatus,
|
||||
const nsresult& status,
|
||||
const uint64_t progress,
|
||||
const uint64_t& progressMax,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count,
|
||||
const nsCString& data);
|
||||
void OnStopRequest(const nsresult& channelStatus, const ResourceTimingStruct& timing);
|
||||
void MaybeDivertOnStop(const nsresult& aChannelStatus);
|
||||
void OnProgress(const int64_t& progress, const int64_t& progressMax);
|
||||
void OnStatus(const nsresult& status);
|
||||
void FailedAsyncOpen(const nsresult& status);
|
||||
void HandleAsyncAbort();
|
||||
void Redirect1Begin(const uint32_t& registrarId,
|
||||
const URIParams& newUri,
|
||||
const uint32_t& redirectFlags,
|
||||
const nsHttpResponseHead& responseHead,
|
||||
const nsACString& securityInfoSerialization,
|
||||
const nsACString& channelId);
|
||||
bool Redirect3Complete(OverrideRunnable* aRunnable);
|
||||
void DeleteSelf();
|
||||
|
||||
// Create a a new channel to be used in a redirection, based on the provided
|
||||
// response headers.
|
||||
nsresult SetupRedirect(nsIURI* uri,
|
||||
const nsHttpResponseHead* responseHead,
|
||||
const uint32_t& redirectFlags,
|
||||
nsIChannel** outChannel);
|
||||
|
||||
// Perform a redirection without communicating with the parent process at all.
|
||||
void BeginNonIPCRedirect(nsIURI* responseURI,
|
||||
const nsHttpResponseHead* responseHead);
|
||||
|
||||
// Override the default security info pointer during a non-IPC redirection.
|
||||
void OverrideSecurityInfoForNonIPCRedirect(nsISupports* securityInfo);
|
||||
|
||||
friend class AssociateApplicationCacheEvent;
|
||||
friend class StartRequestEvent;
|
||||
friend class StopRequestEvent;
|
||||
friend class TransportAndDataEvent;
|
||||
friend class MaybeDivertOnDataHttpEvent;
|
||||
friend class MaybeDivertOnStopHttpEvent;
|
||||
friend class ProgressEvent;
|
||||
friend class StatusEvent;
|
||||
friend class FailedAsyncOpenEvent;
|
||||
friend class Redirect1Event;
|
||||
friend class Redirect3Event;
|
||||
friend class DeleteSelfEvent;
|
||||
friend class HttpAsyncAborter<HttpChannelChild>;
|
||||
friend class InterceptStreamListener;
|
||||
friend class InterceptedChannelContent;
|
||||
};
|
||||
|
||||
// A stream listener interposed between the nsInputStreamPump used for intercepted channels
|
||||
// and this channel's original listener. This is only used to ensure the original listener
|
||||
// sees the channel as the request object, and to synthesize OnStatus and OnProgress notifications.
|
||||
class InterceptStreamListener : public nsIStreamListener
|
||||
, public nsIProgressEventSink
|
||||
{
|
||||
RefPtr<HttpChannelChild> mOwner;
|
||||
nsCOMPtr<nsISupports> mContext;
|
||||
virtual ~InterceptStreamListener() {}
|
||||
public:
|
||||
InterceptStreamListener(HttpChannelChild* aOwner, nsISupports* aContext)
|
||||
: mOwner(aOwner)
|
||||
, mContext(aContext)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIPROGRESSEVENTSINK
|
||||
|
||||
void Cleanup();
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// inline functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline bool
|
||||
HttpChannelChild::IsSuspended()
|
||||
{
|
||||
return mSuspendCount != 0;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_HttpChannelChild_h
|
||||
1821
netwerk/protocol/http/HttpChannelParent.cpp
Normal file
1821
netwerk/protocol/http/HttpChannelParent.cpp
Normal file
File diff suppressed because it is too large
Load diff
269
netwerk/protocol/http/HttpChannelParent.h
Normal file
269
netwerk/protocol/http/HttpChannelParent.h
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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_HttpChannelParent_h
|
||||
#define mozilla_net_HttpChannelParent_h
|
||||
|
||||
#include "ADivertableParentChannel.h"
|
||||
#include "nsHttp.h"
|
||||
#include "mozilla/net/PHttpChannelParent.h"
|
||||
#include "mozilla/net/NeckoCommon.h"
|
||||
#include "mozilla/net/NeckoParent.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIParentRedirectingChannel.h"
|
||||
#include "nsIProgressEventSink.h"
|
||||
#include "nsHttpChannel.h"
|
||||
#include "nsIAuthPromptProvider.h"
|
||||
#include "mozilla/dom/ipc/IdType.h"
|
||||
#include "nsIDeprecationWarner.h"
|
||||
|
||||
class nsICacheEntry;
|
||||
class nsIAssociatedContentSecurity;
|
||||
|
||||
#define HTTP_CHANNEL_PARENT_IID \
|
||||
{ 0x982b2372, 0x7aa5, 0x4e8a, \
|
||||
{ 0xbd, 0x9f, 0x89, 0x74, 0xd7, 0xf0, 0x58, 0xeb } }
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom{
|
||||
class TabParent;
|
||||
class PBrowserOrId;
|
||||
} // namespace dom
|
||||
|
||||
namespace net {
|
||||
|
||||
class HttpChannelParentListener;
|
||||
class ChannelEventQueue;
|
||||
|
||||
// Note: nsIInterfaceRequestor must be the first base so that do_QueryObject()
|
||||
// works correctly on this object, as it's needed to compute a void* pointing to
|
||||
// the beginning of this object.
|
||||
|
||||
class HttpChannelParent final : public nsIInterfaceRequestor
|
||||
, public PHttpChannelParent
|
||||
, public nsIParentRedirectingChannel
|
||||
, public nsIProgressEventSink
|
||||
, public ADivertableParentChannel
|
||||
, public nsIAuthPromptProvider
|
||||
, public nsIDeprecationWarner
|
||||
, public HttpChannelSecurityWarningReporter
|
||||
{
|
||||
virtual ~HttpChannelParent();
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIPARENTCHANNEL
|
||||
NS_DECL_NSIPARENTREDIRECTINGCHANNEL
|
||||
NS_DECL_NSIPROGRESSEVENTSINK
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSIAUTHPROMPTPROVIDER
|
||||
NS_DECL_NSIDEPRECATIONWARNER
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(HTTP_CHANNEL_PARENT_IID)
|
||||
|
||||
HttpChannelParent(const dom::PBrowserOrId& iframeEmbedding,
|
||||
nsILoadContext* aLoadContext,
|
||||
PBOverrideStatus aStatus);
|
||||
|
||||
bool Init(const HttpChannelCreationArgs& aOpenArgs);
|
||||
|
||||
// ADivertableParentChannel functions.
|
||||
void DivertTo(nsIStreamListener *aListener) override;
|
||||
nsresult SuspendForDiversion() override;
|
||||
nsresult SuspendMessageDiversion() override;
|
||||
nsresult ResumeMessageDiversion() override;
|
||||
|
||||
// Calls OnStartRequest for "DivertTo" listener, then notifies child channel
|
||||
// that it should divert OnDataAvailable and OnStopRequest calls to this
|
||||
// parent channel.
|
||||
void StartDiversion();
|
||||
|
||||
// Handles calling OnStart/Stop if there are errors during diversion.
|
||||
// Called asynchronously from FailDiversion.
|
||||
void NotifyDiversionFailed(nsresult aErrorCode, bool aSkipResume = true);
|
||||
|
||||
// Forwarded to nsHttpChannel::SetApplyConversion.
|
||||
void SetApplyConversion(bool aApplyConversion) {
|
||||
if (mChannel) {
|
||||
mChannel->SetApplyConversion(aApplyConversion);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult OpenAlternativeOutputStream(const nsACString & type, nsIOutputStream * *_retval);
|
||||
|
||||
void InvokeAsyncOpen(nsresult rv);
|
||||
protected:
|
||||
// used to connect redirected-to channel in parent with just created
|
||||
// ChildChannel. Used during redirects.
|
||||
bool ConnectChannel(const uint32_t& channelId, const bool& shouldIntercept);
|
||||
|
||||
bool DoAsyncOpen(const URIParams& uri,
|
||||
const OptionalURIParams& originalUri,
|
||||
const OptionalURIParams& docUri,
|
||||
const OptionalURIParams& referrerUri,
|
||||
const uint32_t& referrerPolicy,
|
||||
const OptionalURIParams& internalRedirectUri,
|
||||
const OptionalURIParams& topWindowUri,
|
||||
const uint32_t& loadFlags,
|
||||
const RequestHeaderTuples& requestHeaders,
|
||||
const nsCString& requestMethod,
|
||||
const OptionalIPCStream& uploadStream,
|
||||
const bool& uploadStreamHasHeaders,
|
||||
const uint16_t& priority,
|
||||
const uint32_t& classOfService,
|
||||
const uint8_t& redirectionLimit,
|
||||
const bool& allowPipelining,
|
||||
const bool& allowSTS,
|
||||
const uint32_t& thirdPartyFlags,
|
||||
const bool& doResumeAt,
|
||||
const uint64_t& startPos,
|
||||
const nsCString& entityID,
|
||||
const bool& chooseApplicationCache,
|
||||
const nsCString& appCacheClientID,
|
||||
const bool& allowSpdy,
|
||||
const bool& allowAltSvc,
|
||||
const bool& beConservative,
|
||||
const OptionalLoadInfoArgs& aLoadInfoArgs,
|
||||
const OptionalHttpResponseHead& aSynthesizedResponseHead,
|
||||
const nsCString& aSecurityInfoSerialization,
|
||||
const uint32_t& aCacheKey,
|
||||
const nsCString& aRequestContextID,
|
||||
const OptionalCorsPreflightArgs& aCorsPreflightArgs,
|
||||
const uint32_t& aInitialRwin,
|
||||
const bool& aBlockAuthPrompt,
|
||||
const bool& aSuspendAfterSynthesizeResponse,
|
||||
const bool& aAllowStaleCacheContent,
|
||||
const nsCString& aContentTypeHint,
|
||||
const nsCString& aChannelId,
|
||||
const uint64_t& aContentWindowId,
|
||||
const nsCString& aPreferredAlternativeType);
|
||||
|
||||
virtual bool RecvSetPriority(const uint16_t& priority) override;
|
||||
virtual bool RecvSetClassOfService(const uint32_t& cos) override;
|
||||
virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset) override;
|
||||
virtual bool RecvSuspend() override;
|
||||
virtual bool RecvResume() override;
|
||||
virtual bool RecvCancel(const nsresult& status) override;
|
||||
virtual bool RecvRedirect2Verify(const nsresult& result,
|
||||
const RequestHeaderTuples& changedHeaders,
|
||||
const uint32_t& loadFlags,
|
||||
const OptionalURIParams& apiRedirectUri,
|
||||
const OptionalCorsPreflightArgs& aCorsPreflightArgs,
|
||||
const bool& aForceHSTSPriming,
|
||||
const bool& aMixedContentWouldBlock,
|
||||
const bool& aChooseAppcache) override;
|
||||
virtual bool RecvUpdateAssociatedContentSecurity(const int32_t& broken,
|
||||
const int32_t& no) override;
|
||||
virtual bool RecvDocumentChannelCleanup() override;
|
||||
virtual bool RecvMarkOfflineCacheEntryAsForeign() override;
|
||||
virtual bool RecvDivertOnDataAvailable(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count) override;
|
||||
virtual bool RecvDivertOnStopRequest(const nsresult& statusCode) override;
|
||||
virtual bool RecvDivertComplete() override;
|
||||
virtual bool RecvRemoveCorsPreflightCacheEntry(const URIParams& uri,
|
||||
const mozilla::ipc::PrincipalInfo& requestingPrincipal) override;
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
|
||||
// Supporting function for ADivertableParentChannel.
|
||||
nsresult ResumeForDiversion();
|
||||
|
||||
// Asynchronously calls NotifyDiversionFailed.
|
||||
void FailDiversion(nsresult aErrorCode, bool aSkipResume = true);
|
||||
|
||||
friend class HttpChannelParentListener;
|
||||
RefPtr<mozilla::dom::TabParent> mTabParent;
|
||||
|
||||
nsresult ReportSecurityMessage(const nsAString& aMessageTag,
|
||||
const nsAString& aMessageCategory) override;
|
||||
|
||||
// Calls SendDeleteSelf and sets mIPCClosed to true because we should not
|
||||
// send any more messages after that. Bug 1274886
|
||||
bool DoSendDeleteSelf();
|
||||
// Called to notify the parent channel to not send any more IPC messages.
|
||||
virtual bool RecvDeletingChannel() override;
|
||||
virtual bool RecvFinishInterceptedRedirect() override;
|
||||
|
||||
private:
|
||||
void UpdateAndSerializeSecurityInfo(nsACString& aSerializedSecurityInfoOut);
|
||||
|
||||
void DivertOnDataAvailable(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count);
|
||||
void DivertOnStopRequest(const nsresult& statusCode);
|
||||
void DivertComplete();
|
||||
void MaybeFlushPendingDiversion();
|
||||
void ResponseSynthesized();
|
||||
|
||||
friend class DivertDataAvailableEvent;
|
||||
friend class DivertStopRequestEvent;
|
||||
friend class DivertCompleteEvent;
|
||||
|
||||
RefPtr<nsHttpChannel> mChannel;
|
||||
nsCOMPtr<nsICacheEntry> mCacheEntry;
|
||||
nsCOMPtr<nsIAssociatedContentSecurity> mAssociatedContentSecurity;
|
||||
bool mIPCClosed; // PHttpChannel actor has been Closed()
|
||||
|
||||
nsCOMPtr<nsIChannel> mRedirectChannel;
|
||||
nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
|
||||
|
||||
nsAutoPtr<class nsHttpChannel::OfflineCacheEntryAsForeignMarker> mOfflineForeignMarker;
|
||||
|
||||
// state for combining OnStatus/OnProgress with OnDataAvailable
|
||||
// into one IPDL call to child.
|
||||
nsresult mStoredStatus;
|
||||
int64_t mStoredProgress;
|
||||
int64_t mStoredProgressMax;
|
||||
|
||||
bool mSentRedirect1Begin : 1;
|
||||
bool mSentRedirect1BeginFailed : 1;
|
||||
bool mReceivedRedirect2Verify : 1;
|
||||
|
||||
PBOverrideStatus mPBOverride;
|
||||
|
||||
nsCOMPtr<nsILoadContext> mLoadContext;
|
||||
RefPtr<nsHttpHandler> mHttpHandler;
|
||||
|
||||
RefPtr<HttpChannelParentListener> mParentListener;
|
||||
// The listener we are diverting to or will divert to if mPendingDiversion
|
||||
// is set.
|
||||
nsCOMPtr<nsIStreamListener> mDivertListener;
|
||||
// Set to the canceled status value if the main channel was canceled.
|
||||
nsresult mStatus;
|
||||
// Indicates that diversion has been requested, but we could not start it
|
||||
// yet because the channel is still being opened with a synthesized response.
|
||||
bool mPendingDiversion;
|
||||
// Once set, no OnStart/OnData/OnStop calls should be accepted; conversely, it
|
||||
// must be set when RecvDivertOnData/~DivertOnStop/~DivertComplete are
|
||||
// received from the child channel.
|
||||
bool mDivertingFromChild;
|
||||
|
||||
// Set if OnStart|StopRequest was called during a diversion from the child.
|
||||
bool mDivertedOnStartRequest;
|
||||
|
||||
bool mSuspendedForDiversion;
|
||||
|
||||
// Set if this channel should be suspended after synthesizing a response.
|
||||
bool mSuspendAfterSynthesizeResponse;
|
||||
// Set if this channel will synthesize its response.
|
||||
bool mWillSynthesizeResponse;
|
||||
|
||||
dom::TabId mNestedFrameId;
|
||||
|
||||
RefPtr<ChannelEventQueue> mEventQ;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(HttpChannelParent,
|
||||
HTTP_CHANNEL_PARENT_IID)
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_HttpChannelParent_h
|
||||
407
netwerk/protocol/http/HttpChannelParentListener.cpp
Normal file
407
netwerk/protocol/http/HttpChannelParentListener.cpp
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/. */
|
||||
|
||||
// HttpLog.h should generally be included first
|
||||
#include "HttpLog.h"
|
||||
|
||||
#include "HttpChannelParentListener.h"
|
||||
#include "mozilla/net/HttpChannelParent.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsIAuthPrompt.h"
|
||||
#include "nsIAuthPrompt2.h"
|
||||
#include "nsIHttpEventSink.h"
|
||||
#include "nsIHttpHeaderVisitor.h"
|
||||
#include "nsIRedirectChannelRegistrar.h"
|
||||
#include "nsIPromptFactory.h"
|
||||
#include "nsQueryObject.h"
|
||||
|
||||
using mozilla::Unused;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
HttpChannelParentListener::HttpChannelParentListener(HttpChannelParent* aInitialChannel)
|
||||
: mNextListener(aInitialChannel)
|
||||
, mRedirectChannelId(0)
|
||||
, mSuspendedForDiversion(false)
|
||||
, mShouldIntercept(false)
|
||||
, mShouldSuspendIntercept(false)
|
||||
{
|
||||
}
|
||||
|
||||
HttpChannelParentListener::~HttpChannelParentListener()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelParentListener::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ADDREF(HttpChannelParentListener)
|
||||
NS_IMPL_RELEASE(HttpChannelParentListener)
|
||||
NS_INTERFACE_MAP_BEGIN(HttpChannelParentListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIRedirectResultListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsINetworkInterceptController)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
|
||||
if (aIID.Equals(NS_GET_IID(HttpChannelParentListener))) {
|
||||
foundInterface = static_cast<nsIInterfaceRequestor*>(this);
|
||||
} else
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelParentListener::nsIRequestObserver
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelParentListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mSuspendedForDiversion,
|
||||
"Cannot call OnStartRequest if suspended for diversion!");
|
||||
|
||||
if (!mNextListener)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
LOG(("HttpChannelParentListener::OnStartRequest [this=%p]\n", this));
|
||||
return mNextListener->OnStartRequest(aRequest, aContext);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelParentListener::OnStopRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsresult aStatusCode)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mSuspendedForDiversion,
|
||||
"Cannot call OnStopRequest if suspended for diversion!");
|
||||
|
||||
if (!mNextListener)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
LOG(("HttpChannelParentListener::OnStopRequest: [this=%p status=%ul]\n",
|
||||
this, aStatusCode));
|
||||
nsresult rv = mNextListener->OnStopRequest(aRequest, aContext, aStatusCode);
|
||||
|
||||
mNextListener = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelParentListener::nsIStreamListener
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelParentListener::OnDataAvailable(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsIInputStream *aInputStream,
|
||||
uint64_t aOffset,
|
||||
uint32_t aCount)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mSuspendedForDiversion,
|
||||
"Cannot call OnDataAvailable if suspended for diversion!");
|
||||
|
||||
if (!mNextListener)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
LOG(("HttpChannelParentListener::OnDataAvailable [this=%p]\n", this));
|
||||
return mNextListener->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelParentListener::nsIInterfaceRequestor
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelParentListener::GetInterface(const nsIID& aIID, void **result)
|
||||
{
|
||||
if (aIID.Equals(NS_GET_IID(nsIChannelEventSink)) ||
|
||||
aIID.Equals(NS_GET_IID(nsIHttpEventSink)) ||
|
||||
aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) ||
|
||||
aIID.Equals(NS_GET_IID(nsIRedirectResultListener)))
|
||||
{
|
||||
return QueryInterface(aIID, result);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInterfaceRequestor> ir;
|
||||
if (mNextListener &&
|
||||
NS_SUCCEEDED(CallQueryInterface(mNextListener.get(),
|
||||
getter_AddRefs(ir))))
|
||||
{
|
||||
return ir->GetInterface(aIID, result);
|
||||
}
|
||||
|
||||
if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
|
||||
aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIPromptFactory> wwatch =
|
||||
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return wwatch->GetPrompt(nullptr, aIID,
|
||||
reinterpret_cast<void**>(result));
|
||||
}
|
||||
|
||||
return NS_NOINTERFACE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelParentListener::nsIChannelEventSink
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelParentListener::AsyncOnChannelRedirect(
|
||||
nsIChannel *oldChannel,
|
||||
nsIChannel *newChannel,
|
||||
uint32_t redirectFlags,
|
||||
nsIAsyncVerifyRedirectCallback* callback)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIParentRedirectingChannel> activeRedirectingChannel =
|
||||
do_QueryInterface(mNextListener);
|
||||
if (!activeRedirectingChannel) {
|
||||
NS_ERROR("Channel got a redirect response, but doesn't implement "
|
||||
"nsIParentRedirectingChannel to handle it.");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// Register the new channel and obtain id for it
|
||||
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
|
||||
do_GetService("@mozilla.org/redirectchannelregistrar;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = registrar->RegisterChannel(newChannel, &mRedirectChannelId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Registered %p channel under id=%d", newChannel, mRedirectChannelId));
|
||||
|
||||
return activeRedirectingChannel->StartRedirect(mRedirectChannelId,
|
||||
newChannel,
|
||||
redirectFlags,
|
||||
callback);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelParentListener::nsIRedirectResultListener
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelParentListener::OnRedirectResult(bool succeeded)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIParentChannel> redirectChannel;
|
||||
if (mRedirectChannelId) {
|
||||
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
|
||||
do_GetService("@mozilla.org/redirectchannelregistrar;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = registrar->GetParentChannel(mRedirectChannelId,
|
||||
getter_AddRefs(redirectChannel));
|
||||
if (NS_FAILED(rv) || !redirectChannel) {
|
||||
// Redirect might get canceled before we got AsyncOnChannelRedirect
|
||||
LOG(("Registered parent channel not found under id=%d", mRedirectChannelId));
|
||||
|
||||
nsCOMPtr<nsIChannel> newChannel;
|
||||
rv = registrar->GetRegisteredChannel(mRedirectChannelId,
|
||||
getter_AddRefs(newChannel));
|
||||
MOZ_ASSERT(newChannel, "Already registered channel not found");
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
newChannel->Cancel(NS_BINDING_ABORTED);
|
||||
}
|
||||
|
||||
// Release all previously registered channels, they are no longer need to be
|
||||
// kept in the registrar from this moment.
|
||||
registrar->DeregisterChannels(mRedirectChannelId);
|
||||
|
||||
mRedirectChannelId = 0;
|
||||
}
|
||||
|
||||
if (!redirectChannel) {
|
||||
succeeded = false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIParentRedirectingChannel> activeRedirectingChannel =
|
||||
do_QueryInterface(mNextListener);
|
||||
MOZ_ASSERT(activeRedirectingChannel,
|
||||
"Channel finished a redirect response, but doesn't implement "
|
||||
"nsIParentRedirectingChannel to complete it.");
|
||||
|
||||
if (activeRedirectingChannel) {
|
||||
activeRedirectingChannel->CompleteRedirect(succeeded);
|
||||
} else {
|
||||
succeeded = false;
|
||||
}
|
||||
|
||||
if (succeeded) {
|
||||
// Switch to redirect channel and delete the old one.
|
||||
nsCOMPtr<nsIParentChannel> parent;
|
||||
parent = do_QueryInterface(mNextListener);
|
||||
MOZ_ASSERT(parent);
|
||||
parent->Delete();
|
||||
mNextListener = do_QueryInterface(redirectChannel);
|
||||
MOZ_ASSERT(mNextListener);
|
||||
redirectChannel->SetParentListener(this);
|
||||
} else if (redirectChannel) {
|
||||
// Delete the redirect target channel: continue using old channel
|
||||
redirectChannel->Delete();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelParentListener::nsINetworkInterceptController
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelParentListener::ShouldPrepareForIntercept(nsIURI* aURI,
|
||||
bool aIsNonSubresourceRequest,
|
||||
bool* aShouldIntercept)
|
||||
{
|
||||
*aShouldIntercept = mShouldIntercept;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class HeaderVisitor final : public nsIHttpHeaderVisitor
|
||||
{
|
||||
nsCOMPtr<nsIInterceptedChannel> mChannel;
|
||||
~HeaderVisitor()
|
||||
{
|
||||
}
|
||||
public:
|
||||
explicit HeaderVisitor(nsIInterceptedChannel* aChannel) : mChannel(aChannel)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD VisitHeader(const nsACString& aHeader, const nsACString& aValue) override
|
||||
{
|
||||
mChannel->SynthesizeHeader(aHeader, aValue);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(HeaderVisitor, nsIHttpHeaderVisitor)
|
||||
|
||||
class FinishSynthesizedResponse : public Runnable
|
||||
{
|
||||
nsCOMPtr<nsIInterceptedChannel> mChannel;
|
||||
public:
|
||||
explicit FinishSynthesizedResponse(nsIInterceptedChannel* aChannel)
|
||||
: mChannel(aChannel)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
// The URL passed as an argument here doesn't matter, since the child will
|
||||
// receive a redirection notification as a result of this synthesized response.
|
||||
mChannel->FinishSynthesizedResponse(EmptyCString());
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelParentListener::ChannelIntercepted(nsIInterceptedChannel* aChannel)
|
||||
{
|
||||
if (mShouldSuspendIntercept) {
|
||||
mInterceptedChannel = aChannel;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString statusText;
|
||||
mSynthesizedResponseHead->StatusText(statusText);
|
||||
aChannel->SynthesizeStatus(mSynthesizedResponseHead->Status(), statusText);
|
||||
nsCOMPtr<nsIHttpHeaderVisitor> visitor = new HeaderVisitor(aChannel);
|
||||
mSynthesizedResponseHead->VisitHeaders(visitor,
|
||||
nsHttpHeaderArray::eFilterResponse);
|
||||
|
||||
nsCOMPtr<nsIRunnable> event = new FinishSynthesizedResponse(aChannel);
|
||||
NS_DispatchToCurrentThread(event);
|
||||
|
||||
mSynthesizedResponseHead = nullptr;
|
||||
|
||||
MOZ_ASSERT(mNextListener);
|
||||
RefPtr<HttpChannelParent> channel = do_QueryObject(mNextListener);
|
||||
MOZ_ASSERT(channel);
|
||||
channel->ResponseSynthesized();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
HttpChannelParentListener::SuspendForDiversion()
|
||||
{
|
||||
if (NS_WARN_IF(mSuspendedForDiversion)) {
|
||||
MOZ_ASSERT(!mSuspendedForDiversion, "Cannot SuspendForDiversion twice!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// While this is set, no OnStart/OnData/OnStop callbacks should be forwarded
|
||||
// to mNextListener.
|
||||
mSuspendedForDiversion = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HttpChannelParentListener::ResumeForDiversion()
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mSuspendedForDiversion, "Must already be suspended!");
|
||||
|
||||
// Allow OnStart/OnData/OnStop callbacks to be forwarded to mNextListener.
|
||||
mSuspendedForDiversion = false;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HttpChannelParentListener::DivertTo(nsIStreamListener* aListener)
|
||||
{
|
||||
MOZ_ASSERT(aListener);
|
||||
MOZ_RELEASE_ASSERT(mSuspendedForDiversion, "Must already be suspended!");
|
||||
|
||||
mNextListener = aListener;
|
||||
|
||||
return ResumeForDiversion();
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelParentListener::SetupInterception(const nsHttpResponseHead& aResponseHead)
|
||||
{
|
||||
mSynthesizedResponseHead = new nsHttpResponseHead(aResponseHead);
|
||||
mShouldIntercept = true;
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelParentListener::SetupInterceptionAfterRedirect(bool aShouldIntercept)
|
||||
{
|
||||
mShouldIntercept = aShouldIntercept;
|
||||
if (mShouldIntercept) {
|
||||
// When an interception occurs, this channel should suspend all further activity.
|
||||
// It will be torn down and recreated if necessary.
|
||||
mShouldSuspendIntercept = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelParentListener::ClearInterceptedChannel()
|
||||
{
|
||||
if (mInterceptedChannel) {
|
||||
mInterceptedChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
|
||||
mInterceptedChannel = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
89
netwerk/protocol/http/HttpChannelParentListener.h
Normal file
89
netwerk/protocol/http/HttpChannelParentListener.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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_HttpChannelCallbackWrapper_h
|
||||
#define mozilla_net_HttpChannelCallbackWrapper_h
|
||||
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
#include "nsIRedirectResultListener.h"
|
||||
#include "nsINetworkInterceptController.h"
|
||||
#include "nsIStreamListener.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class HttpChannelParent;
|
||||
|
||||
#define HTTP_CHANNEL_PARENT_LISTENER_IID \
|
||||
{ 0xe409da52, 0xda76, 0x4eb7, \
|
||||
{ 0xa7, 0xf4, 0x03, 0x3d, 0x88, 0xac, 0x87, 0x6d } }
|
||||
|
||||
// Note: nsIInterfaceRequestor must be the first base so that do_QueryObject()
|
||||
// works correctly on this object, as it's needed to compute a void* pointing to
|
||||
// the beginning of this object.
|
||||
|
||||
class HttpChannelParentListener final : public nsIInterfaceRequestor
|
||||
, public nsIChannelEventSink
|
||||
, public nsIRedirectResultListener
|
||||
, public nsIStreamListener
|
||||
, public nsINetworkInterceptController
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
NS_DECL_NSIREDIRECTRESULTLISTENER
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSINETWORKINTERCEPTCONTROLLER
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(HTTP_CHANNEL_PARENT_LISTENER_IID)
|
||||
|
||||
explicit HttpChannelParentListener(HttpChannelParent* aInitialChannel);
|
||||
|
||||
// For channel diversion from child to parent.
|
||||
nsresult DivertTo(nsIStreamListener *aListener);
|
||||
nsresult SuspendForDiversion();
|
||||
|
||||
void SetupInterception(const nsHttpResponseHead& aResponseHead);
|
||||
void SetupInterceptionAfterRedirect(bool aShouldIntercept);
|
||||
void ClearInterceptedChannel();
|
||||
|
||||
private:
|
||||
virtual ~HttpChannelParentListener();
|
||||
|
||||
// Private partner function to SuspendForDiversion.
|
||||
nsresult ResumeForDiversion();
|
||||
|
||||
// Can be the original HttpChannelParent that created this object (normal
|
||||
// case), a different {HTTP|FTP}ChannelParent that we've been redirected to,
|
||||
// or some other listener that we have been diverted to via
|
||||
// nsIDivertableChannel.
|
||||
nsCOMPtr<nsIStreamListener> mNextListener;
|
||||
uint32_t mRedirectChannelId;
|
||||
// When set, no OnStart/OnData/OnStop calls should be received.
|
||||
bool mSuspendedForDiversion;
|
||||
|
||||
// Set if this channel should be intercepted before it sets up the HTTP transaction.
|
||||
bool mShouldIntercept;
|
||||
// Set if this channel should suspend on interception.
|
||||
bool mShouldSuspendIntercept;
|
||||
|
||||
nsAutoPtr<nsHttpResponseHead> mSynthesizedResponseHead;
|
||||
|
||||
// Handle to the channel wrapper if this channel has been intercepted.
|
||||
nsCOMPtr<nsIInterceptedChannel> mInterceptedChannel;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(HttpChannelParentListener,
|
||||
HTTP_CHANNEL_PARENT_LISTENER_IID)
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_HttpChannelParent_h
|
||||
18
netwerk/protocol/http/HttpInfo.cpp
Normal file
18
netwerk/protocol/http/HttpInfo.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/* 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/. */
|
||||
|
||||
// HttpLog.h should generally be included first
|
||||
#include "HttpLog.h"
|
||||
|
||||
#include "nsHttpHandler.h"
|
||||
#include "HttpInfo.h"
|
||||
|
||||
|
||||
void
|
||||
mozilla::net::HttpInfo::
|
||||
GetHttpConnectionData(nsTArray<HttpRetParams>* args)
|
||||
{
|
||||
if (gHttpHandler)
|
||||
gHttpHandler->ConnMgr()->GetConnectionData(args);
|
||||
}
|
||||
25
netwerk/protocol/http/HttpInfo.h
Normal file
25
netwerk/protocol/http/HttpInfo.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* 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 nsHttpInfo__
|
||||
#define nsHttpInfo__
|
||||
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
struct HttpRetParams;
|
||||
|
||||
class HttpInfo
|
||||
{
|
||||
public:
|
||||
/* Calls getConnectionData method in nsHttpConnectionMgr. */
|
||||
static void GetHttpConnectionData(nsTArray<HttpRetParams> *);
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // nsHttpInfo__
|
||||
58
netwerk/protocol/http/HttpLog.h
Normal file
58
netwerk/protocol/http/HttpLog.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:set ts=4 sw=4 sts=4 et cin: */
|
||||
/* 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 HttpLog_h__
|
||||
#define HttpLog_h__
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* This file should ONLY be #included by source (.cpp) files in the /http
|
||||
* directory, not headers (.h). If you need to use LOG() in a .h file, call
|
||||
* PR_LOG directly.
|
||||
*
|
||||
* This file should also be the first #include in your file.
|
||||
*
|
||||
* Yes, this is kludgy.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
|
||||
// Get rid of Chromium's LOG definition
|
||||
#undef LOG
|
||||
|
||||
//
|
||||
// Log module for HTTP Protocol logging...
|
||||
//
|
||||
// To enable logging (see prlog.h for full details):
|
||||
//
|
||||
// set MOZ_LOG=nsHttp:5
|
||||
// set MOZ_LOG_FILE=http.log
|
||||
//
|
||||
// This enables LogLevel::Debug level information and places all output in
|
||||
// the file http.log.
|
||||
//
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
extern LazyLogModule gHttpLog;
|
||||
}
|
||||
}
|
||||
|
||||
// http logging
|
||||
#define LOG1(args) MOZ_LOG(mozilla::net::gHttpLog, mozilla::LogLevel::Error, args)
|
||||
#define LOG2(args) MOZ_LOG(mozilla::net::gHttpLog, mozilla::LogLevel::Warning, args)
|
||||
#define LOG3(args) MOZ_LOG(mozilla::net::gHttpLog, mozilla::LogLevel::Info, args)
|
||||
#define LOG4(args) MOZ_LOG(mozilla::net::gHttpLog, mozilla::LogLevel::Debug, args)
|
||||
#define LOG5(args) MOZ_LOG(mozilla::net::gHttpLog, mozilla::LogLevel::Verbose, args)
|
||||
#define LOG(args) LOG4(args)
|
||||
|
||||
#define LOG1_ENABLED() MOZ_LOG_TEST(mozilla::net::gHttpLog, mozilla::LogLevel::Error)
|
||||
#define LOG2_ENABLED() MOZ_LOG_TEST(mozilla::net::gHttpLog, mozilla::LogLevel::Warning)
|
||||
#define LOG3_ENABLED() MOZ_LOG_TEST(mozilla::net::gHttpLog, mozilla::LogLevel::Info)
|
||||
#define LOG4_ENABLED() MOZ_LOG_TEST(mozilla::net::gHttpLog, mozilla::LogLevel::Debug)
|
||||
#define LOG5_ENABLED() MOZ_LOG_TEST(mozilla::net::gHttpLog, mozilla::LogLevel::Verbose)
|
||||
#define LOG_ENABLED() LOG4_ENABLED()
|
||||
|
||||
#endif // HttpLog_h__
|
||||
540
netwerk/protocol/http/InterceptedChannel.cpp
Normal file
540
netwerk/protocol/http/InterceptedChannel.cpp
Normal file
|
|
@ -0,0 +1,540 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: -*- */
|
||||
/* vim:set expandtab ts=2 sw=2 sts=2 cin: */
|
||||
/* 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/. */
|
||||
|
||||
#include "HttpLog.h"
|
||||
|
||||
#include "InterceptedChannel.h"
|
||||
#include "nsInputStreamPump.h"
|
||||
#include "nsIPipe.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsHttpChannel.h"
|
||||
#include "HttpChannelChild.h"
|
||||
#include "nsHttpResponseHead.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "mozilla/ConsoleReportCollector.h"
|
||||
#include "mozilla/dom/ChannelInfo.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
extern bool
|
||||
WillRedirect(const nsHttpResponseHead * response);
|
||||
|
||||
extern nsresult
|
||||
DoUpdateExpirationTime(nsHttpChannel* aSelf,
|
||||
nsICacheEntry* aCacheEntry,
|
||||
nsHttpResponseHead* aResponseHead,
|
||||
uint32_t& aExpirationTime);
|
||||
extern nsresult
|
||||
DoAddCacheEntryHeaders(nsHttpChannel *self,
|
||||
nsICacheEntry *entry,
|
||||
nsHttpRequestHead *requestHead,
|
||||
nsHttpResponseHead *responseHead,
|
||||
nsISupports *securityInfo);
|
||||
|
||||
NS_IMPL_ISUPPORTS(InterceptedChannelBase, nsIInterceptedChannel)
|
||||
|
||||
InterceptedChannelBase::InterceptedChannelBase(nsINetworkInterceptController* aController)
|
||||
: mController(aController)
|
||||
, mReportCollector(new ConsoleReportCollector())
|
||||
, mClosed(false)
|
||||
{
|
||||
}
|
||||
|
||||
InterceptedChannelBase::~InterceptedChannelBase()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelBase::GetResponseBody(nsIOutputStream** aStream)
|
||||
{
|
||||
NS_IF_ADDREF(*aStream = mResponseBody);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
InterceptedChannelBase::EnsureSynthesizedResponse()
|
||||
{
|
||||
if (mSynthesizedResponseHead.isNothing()) {
|
||||
mSynthesizedResponseHead.emplace(new nsHttpResponseHead());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InterceptedChannelBase::DoNotifyController()
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (NS_WARN_IF(!mController)) {
|
||||
rv = ResetInterception();
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"Failed to resume intercepted network request");
|
||||
return;
|
||||
}
|
||||
|
||||
rv = mController->ChannelIntercepted(this);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
rv = ResetInterception();
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"Failed to resume intercepted network request");
|
||||
}
|
||||
mController = nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
InterceptedChannelBase::DoSynthesizeStatus(uint16_t aStatus, const nsACString& aReason)
|
||||
{
|
||||
EnsureSynthesizedResponse();
|
||||
|
||||
// Always assume HTTP 1.1 for synthesized responses.
|
||||
nsAutoCString statusLine;
|
||||
statusLine.AppendLiteral("HTTP/1.1 ");
|
||||
statusLine.AppendInt(aStatus);
|
||||
statusLine.AppendLiteral(" ");
|
||||
statusLine.Append(aReason);
|
||||
|
||||
(*mSynthesizedResponseHead)->ParseStatusLine(statusLine);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
InterceptedChannelBase::DoSynthesizeHeader(const nsACString& aName, const nsACString& aValue)
|
||||
{
|
||||
EnsureSynthesizedResponse();
|
||||
|
||||
nsAutoCString header = aName + NS_LITERAL_CSTRING(": ") + aValue;
|
||||
// Overwrite any existing header.
|
||||
nsresult rv = (*mSynthesizedResponseHead)->ParseHeaderLine(header);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelBase::GetConsoleReportCollector(nsIConsoleReportCollector** aCollectorOut)
|
||||
{
|
||||
MOZ_ASSERT(aCollectorOut);
|
||||
nsCOMPtr<nsIConsoleReportCollector> ref = mReportCollector;
|
||||
ref.forget(aCollectorOut);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelBase::SetReleaseHandle(nsISupports* aHandle)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mReleaseHandle);
|
||||
MOZ_ASSERT(aHandle);
|
||||
|
||||
// We need to keep it and mChannel alive until destructor clear it up.
|
||||
mReleaseHandle = aHandle;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<nsIURI>
|
||||
InterceptedChannelBase::SecureUpgradeChannelURI(nsIChannel* aChannel)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
nsCOMPtr<nsIURI> upgradedURI;
|
||||
rv = NS_GetSecureUpgradedURI(uri, getter_AddRefs(upgradedURI));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
return upgradedURI.forget();
|
||||
}
|
||||
|
||||
InterceptedChannelChrome::InterceptedChannelChrome(nsHttpChannel* aChannel,
|
||||
nsINetworkInterceptController* aController,
|
||||
nsICacheEntry* aEntry)
|
||||
: InterceptedChannelBase(aController)
|
||||
, mChannel(aChannel)
|
||||
, mSynthesizedCacheEntry(aEntry)
|
||||
{
|
||||
nsresult rv = mChannel->GetApplyConversion(&mOldApplyConversion);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mOldApplyConversion = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InterceptedChannelChrome::NotifyController()
|
||||
{
|
||||
// Intercepted responses should already be decoded.
|
||||
mChannel->SetApplyConversion(false);
|
||||
|
||||
nsresult rv = mSynthesizedCacheEntry->OpenOutputStream(0, getter_AddRefs(mResponseBody));
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
DoNotifyController();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelChrome::GetChannel(nsIChannel** aChannel)
|
||||
{
|
||||
NS_IF_ADDREF(*aChannel = mChannel);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelChrome::ResetInterception()
|
||||
{
|
||||
if (mClosed) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
mReportCollector->FlushConsoleReports(mChannel);
|
||||
|
||||
mSynthesizedCacheEntry->AsyncDoom(nullptr);
|
||||
mSynthesizedCacheEntry = nullptr;
|
||||
|
||||
mChannel->SetApplyConversion(mOldApplyConversion);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
mChannel->GetURI(getter_AddRefs(uri));
|
||||
|
||||
nsresult rv = mChannel->StartRedirectChannelToURI(uri, nsIChannelEventSink::REDIRECT_INTERNAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mResponseBody->Close();
|
||||
mResponseBody = nullptr;
|
||||
mClosed = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelChrome::SynthesizeStatus(uint16_t aStatus, const nsACString& aReason)
|
||||
{
|
||||
if (!mSynthesizedCacheEntry) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return DoSynthesizeStatus(aStatus, aReason);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelChrome::SynthesizeHeader(const nsACString& aName, const nsACString& aValue)
|
||||
{
|
||||
if (!mSynthesizedCacheEntry) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return DoSynthesizeHeader(aName, aValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelChrome::FinishSynthesizedResponse(const nsACString& aFinalURLSpec)
|
||||
{
|
||||
if (mClosed) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// Make sure the cache entry's output stream is always closed. If the
|
||||
// channel was intercepted with a null-body response then its possible
|
||||
// the synthesis completed without a stream copy operation.
|
||||
mResponseBody->Close();
|
||||
|
||||
mReportCollector->FlushConsoleReports(mChannel);
|
||||
|
||||
EnsureSynthesizedResponse();
|
||||
|
||||
// If the synthesized response is a redirect, then we want to respect
|
||||
// the encoding of whatever is loaded as a result.
|
||||
if (WillRedirect(mSynthesizedResponseHead.ref())) {
|
||||
nsresult rv = mChannel->SetApplyConversion(mOldApplyConversion);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
mChannel->MarkIntercepted();
|
||||
|
||||
// First we ensure the appropriate metadata is set on the synthesized cache entry
|
||||
// (i.e. the flattened response head)
|
||||
|
||||
nsCOMPtr<nsISupports> securityInfo;
|
||||
nsresult rv = mChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uint32_t expirationTime = 0;
|
||||
rv = DoUpdateExpirationTime(mChannel, mSynthesizedCacheEntry,
|
||||
mSynthesizedResponseHead.ref(),
|
||||
expirationTime);
|
||||
|
||||
rv = DoAddCacheEntryHeaders(mChannel, mSynthesizedCacheEntry,
|
||||
mChannel->GetRequestHead(),
|
||||
mSynthesizedResponseHead.ref(), securityInfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURI> originalURI;
|
||||
mChannel->GetURI(getter_AddRefs(originalURI));
|
||||
|
||||
nsCOMPtr<nsIURI> responseURI;
|
||||
if (!aFinalURLSpec.IsEmpty()) {
|
||||
rv = NS_NewURI(getter_AddRefs(responseURI), aFinalURLSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
responseURI = originalURI;
|
||||
}
|
||||
|
||||
bool equal = false;
|
||||
originalURI->Equals(responseURI, &equal);
|
||||
if (!equal) {
|
||||
rv =
|
||||
mChannel->StartRedirectChannelToURI(responseURI, nsIChannelEventSink::REDIRECT_INTERNAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
bool usingSSL = false;
|
||||
responseURI->SchemeIs("https", &usingSSL);
|
||||
|
||||
// Then we open a real cache entry to read the synthesized response from.
|
||||
rv = mChannel->OpenCacheEntry(usingSSL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mSynthesizedCacheEntry = nullptr;
|
||||
|
||||
if (!mChannel->AwaitingCacheCallbacks()) {
|
||||
rv = mChannel->ContinueConnect();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
mClosed = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelChrome::Cancel(nsresult aStatus)
|
||||
{
|
||||
MOZ_ASSERT(NS_FAILED(aStatus));
|
||||
|
||||
if (mClosed) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mReportCollector->FlushConsoleReports(mChannel);
|
||||
|
||||
// we need to use AsyncAbort instead of Cancel since there's no active pump
|
||||
// to cancel which will provide OnStart/OnStopRequest to the channel.
|
||||
nsresult rv = mChannel->AsyncAbort(aStatus);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mClosed = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelChrome::SetChannelInfo(dom::ChannelInfo* aChannelInfo)
|
||||
{
|
||||
if (mClosed) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return aChannelInfo->ResurrectInfoOnChannel(mChannel);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelChrome::GetInternalContentPolicyType(nsContentPolicyType* aPolicyType)
|
||||
{
|
||||
NS_ENSURE_ARG(aPolicyType);
|
||||
nsCOMPtr<nsILoadInfo> loadInfo;
|
||||
nsresult rv = mChannel->GetLoadInfo(getter_AddRefs(loadInfo));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aPolicyType = loadInfo->InternalContentPolicyType();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelChrome::GetSecureUpgradedChannelURI(nsIURI** aURI)
|
||||
{
|
||||
return mChannel->GetURI(aURI);
|
||||
}
|
||||
|
||||
InterceptedChannelContent::InterceptedChannelContent(HttpChannelChild* aChannel,
|
||||
nsINetworkInterceptController* aController,
|
||||
InterceptStreamListener* aListener,
|
||||
bool aSecureUpgrade)
|
||||
: InterceptedChannelBase(aController)
|
||||
, mChannel(aChannel)
|
||||
, mStreamListener(aListener)
|
||||
, mSecureUpgrade(aSecureUpgrade)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
InterceptedChannelContent::NotifyController()
|
||||
{
|
||||
nsresult rv = NS_NewPipe(getter_AddRefs(mSynthesizedInput),
|
||||
getter_AddRefs(mResponseBody),
|
||||
0, UINT32_MAX, true, true);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
DoNotifyController();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelContent::GetChannel(nsIChannel** aChannel)
|
||||
{
|
||||
NS_IF_ADDREF(*aChannel = mChannel);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelContent::ResetInterception()
|
||||
{
|
||||
if (mClosed) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
mReportCollector->FlushConsoleReports(mChannel);
|
||||
|
||||
mResponseBody->Close();
|
||||
mResponseBody = nullptr;
|
||||
mSynthesizedInput = nullptr;
|
||||
|
||||
mChannel->ResetInterception();
|
||||
|
||||
mClosed = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelContent::SynthesizeStatus(uint16_t aStatus, const nsACString& aReason)
|
||||
{
|
||||
if (!mResponseBody) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return DoSynthesizeStatus(aStatus, aReason);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelContent::SynthesizeHeader(const nsACString& aName, const nsACString& aValue)
|
||||
{
|
||||
if (!mResponseBody) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return DoSynthesizeHeader(aName, aValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelContent::FinishSynthesizedResponse(const nsACString& aFinalURLSpec)
|
||||
{
|
||||
if (NS_WARN_IF(mClosed)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// Make sure the body output stream is always closed. If the channel was
|
||||
// intercepted with a null-body response then its possible the synthesis
|
||||
// completed without a stream copy operation.
|
||||
mResponseBody->Close();
|
||||
|
||||
mReportCollector->FlushConsoleReports(mChannel);
|
||||
|
||||
EnsureSynthesizedResponse();
|
||||
|
||||
nsCOMPtr<nsIURI> originalURI;
|
||||
mChannel->GetURI(getter_AddRefs(originalURI));
|
||||
|
||||
nsCOMPtr<nsIURI> responseURI;
|
||||
if (!aFinalURLSpec.IsEmpty()) {
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(responseURI), aFinalURLSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (mSecureUpgrade) {
|
||||
nsresult rv = NS_GetSecureUpgradedURI(originalURI,
|
||||
getter_AddRefs(responseURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
responseURI = originalURI;
|
||||
}
|
||||
|
||||
bool equal = false;
|
||||
originalURI->Equals(responseURI, &equal);
|
||||
if (!equal) {
|
||||
mChannel->ForceIntercepted(mSynthesizedInput);
|
||||
mChannel->BeginNonIPCRedirect(responseURI, *mSynthesizedResponseHead.ptr());
|
||||
} else {
|
||||
mChannel->OverrideWithSynthesizedResponse(mSynthesizedResponseHead.ref(),
|
||||
mSynthesizedInput,
|
||||
mStreamListener);
|
||||
}
|
||||
|
||||
mResponseBody = nullptr;
|
||||
mStreamListener = nullptr;
|
||||
mClosed = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelContent::Cancel(nsresult aStatus)
|
||||
{
|
||||
MOZ_ASSERT(NS_FAILED(aStatus));
|
||||
|
||||
if (mClosed) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mReportCollector->FlushConsoleReports(mChannel);
|
||||
|
||||
// we need to use AsyncAbort instead of Cancel since there's no active pump
|
||||
// to cancel which will provide OnStart/OnStopRequest to the channel.
|
||||
nsresult rv = mChannel->AsyncAbort(aStatus);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mStreamListener = nullptr;
|
||||
mClosed = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelContent::SetChannelInfo(dom::ChannelInfo* aChannelInfo)
|
||||
{
|
||||
if (mClosed) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return aChannelInfo->ResurrectInfoOnChannel(mChannel);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelContent::GetInternalContentPolicyType(nsContentPolicyType* aPolicyType)
|
||||
{
|
||||
NS_ENSURE_ARG(aPolicyType);
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo;
|
||||
nsresult rv = mChannel->GetLoadInfo(getter_AddRefs(loadInfo));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aPolicyType = loadInfo->InternalContentPolicyType();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
InterceptedChannelContent::GetSecureUpgradedChannelURI(nsIURI** aURI)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
if (mSecureUpgrade) {
|
||||
uri = SecureUpgradeChannelURI(mChannel);
|
||||
} else {
|
||||
nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
if (uri) {
|
||||
uri.forget(aURI);
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
134
netwerk/protocol/http/InterceptedChannel.h
Normal file
134
netwerk/protocol/http/InterceptedChannel.h
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set expandtab ts=2 sw=2 sts=2 cin: */
|
||||
/* 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 InterceptedChannel_h
|
||||
#define InterceptedChannel_h
|
||||
|
||||
#include "nsINetworkInterceptController.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
class nsICacheEntry;
|
||||
class nsInputStreamPump;
|
||||
class nsIStreamListener;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class nsHttpChannel;
|
||||
class HttpChannelChild;
|
||||
class nsHttpResponseHead;
|
||||
class InterceptStreamListener;
|
||||
|
||||
// An object representing a channel that has been intercepted. This avoids complicating
|
||||
// the actual channel implementation with the details of synthesizing responses.
|
||||
class InterceptedChannelBase : public nsIInterceptedChannel {
|
||||
protected:
|
||||
// The interception controller to notify about the successful channel interception
|
||||
nsCOMPtr<nsINetworkInterceptController> mController;
|
||||
|
||||
// The stream to write the body of the synthesized response
|
||||
nsCOMPtr<nsIOutputStream> mResponseBody;
|
||||
|
||||
// Response head for use when synthesizing
|
||||
Maybe<nsAutoPtr<nsHttpResponseHead>> mSynthesizedResponseHead;
|
||||
|
||||
nsCOMPtr<nsIConsoleReportCollector> mReportCollector;
|
||||
nsCOMPtr<nsISupports> mReleaseHandle;
|
||||
|
||||
bool mClosed;
|
||||
|
||||
void EnsureSynthesizedResponse();
|
||||
void DoNotifyController();
|
||||
nsresult DoSynthesizeStatus(uint16_t aStatus, const nsACString& aReason);
|
||||
nsresult DoSynthesizeHeader(const nsACString& aName, const nsACString& aValue);
|
||||
|
||||
virtual ~InterceptedChannelBase();
|
||||
public:
|
||||
explicit InterceptedChannelBase(nsINetworkInterceptController* aController);
|
||||
|
||||
// Notify the interception controller that the channel has been intercepted
|
||||
// and prepare the response body output stream.
|
||||
virtual void NotifyController() = 0;
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD GetResponseBody(nsIOutputStream** aOutput) override;
|
||||
NS_IMETHOD GetConsoleReportCollector(nsIConsoleReportCollector** aCollectorOut) override;
|
||||
NS_IMETHOD SetReleaseHandle(nsISupports* aHandle) override;
|
||||
|
||||
static already_AddRefed<nsIURI>
|
||||
SecureUpgradeChannelURI(nsIChannel* aChannel);
|
||||
};
|
||||
|
||||
class InterceptedChannelChrome : public InterceptedChannelBase
|
||||
{
|
||||
// The actual channel being intercepted.
|
||||
RefPtr<nsHttpChannel> mChannel;
|
||||
|
||||
// Writeable cache entry for use when synthesizing a response in a parent process
|
||||
nsCOMPtr<nsICacheEntry> mSynthesizedCacheEntry;
|
||||
|
||||
// When a channel is intercepted, content decoding is disabled since the
|
||||
// ServiceWorker will have already extracted the decoded data. For parent
|
||||
// process channels we need to preserve the earlier value in case
|
||||
// ResetInterception is called.
|
||||
bool mOldApplyConversion;
|
||||
public:
|
||||
InterceptedChannelChrome(nsHttpChannel* aChannel,
|
||||
nsINetworkInterceptController* aController,
|
||||
nsICacheEntry* aEntry);
|
||||
|
||||
NS_IMETHOD ResetInterception() override;
|
||||
NS_IMETHOD FinishSynthesizedResponse(const nsACString& aFinalURLSpec) override;
|
||||
NS_IMETHOD GetChannel(nsIChannel** aChannel) override;
|
||||
NS_IMETHOD GetSecureUpgradedChannelURI(nsIURI** aURI) override;
|
||||
NS_IMETHOD SynthesizeStatus(uint16_t aStatus, const nsACString& aReason) override;
|
||||
NS_IMETHOD SynthesizeHeader(const nsACString& aName, const nsACString& aValue) override;
|
||||
NS_IMETHOD Cancel(nsresult aStatus) override;
|
||||
NS_IMETHOD SetChannelInfo(mozilla::dom::ChannelInfo* aChannelInfo) override;
|
||||
NS_IMETHOD GetInternalContentPolicyType(nsContentPolicyType *aInternalContentPolicyType) override;
|
||||
|
||||
virtual void NotifyController() override;
|
||||
};
|
||||
|
||||
class InterceptedChannelContent : public InterceptedChannelBase
|
||||
{
|
||||
// The actual channel being intercepted.
|
||||
RefPtr<HttpChannelChild> mChannel;
|
||||
|
||||
// Reader-side of the response body when synthesizing in a child proces
|
||||
nsCOMPtr<nsIInputStream> mSynthesizedInput;
|
||||
|
||||
// Listener for the synthesized response to fix up the notifications before they reach
|
||||
// the actual channel.
|
||||
RefPtr<InterceptStreamListener> mStreamListener;
|
||||
|
||||
// Set for intercepted channels that have gone through a secure upgrade.
|
||||
bool mSecureUpgrade;
|
||||
public:
|
||||
InterceptedChannelContent(HttpChannelChild* aChannel,
|
||||
nsINetworkInterceptController* aController,
|
||||
InterceptStreamListener* aListener,
|
||||
bool aSecureUpgrade);
|
||||
|
||||
NS_IMETHOD ResetInterception() override;
|
||||
NS_IMETHOD FinishSynthesizedResponse(const nsACString& aFinalURLSpec) override;
|
||||
NS_IMETHOD GetChannel(nsIChannel** aChannel) override;
|
||||
NS_IMETHOD GetSecureUpgradedChannelURI(nsIURI** aURI) override;
|
||||
NS_IMETHOD SynthesizeStatus(uint16_t aStatus, const nsACString& aReason) override;
|
||||
NS_IMETHOD SynthesizeHeader(const nsACString& aName, const nsACString& aValue) override;
|
||||
NS_IMETHOD Cancel(nsresult aStatus) override;
|
||||
NS_IMETHOD SetChannelInfo(mozilla::dom::ChannelInfo* aChannelInfo) override;
|
||||
NS_IMETHOD GetInternalContentPolicyType(nsContentPolicyType *aInternalContentPolicyType) override;
|
||||
|
||||
virtual void NotifyController() override;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // InterceptedChannel_h
|
||||
772
netwerk/protocol/http/NullHttpChannel.cpp
Normal file
772
netwerk/protocol/http/NullHttpChannel.cpp
Normal file
|
|
@ -0,0 +1,772 @@
|
|||
/* 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/. */
|
||||
|
||||
#include "NullHttpChannel.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsContentSecurityManager.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIStreamListener.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
NS_IMPL_ISUPPORTS(NullHttpChannel, nsINullChannel,
|
||||
nsIHttpChannel, nsITimedChannel)
|
||||
|
||||
NullHttpChannel::NullHttpChannel()
|
||||
{
|
||||
mChannelCreationTime = PR_Now();
|
||||
mChannelCreationTimestamp = TimeStamp::Now();
|
||||
mAsyncOpenTime = TimeStamp::Now();
|
||||
}
|
||||
|
||||
NullHttpChannel::NullHttpChannel(nsIHttpChannel * chan)
|
||||
{
|
||||
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
||||
ssm->GetChannelURIPrincipal(chan, getter_AddRefs(mResourcePrincipal));
|
||||
|
||||
chan->GetResponseHeader(NS_LITERAL_CSTRING("Timing-Allow-Origin"),
|
||||
mTimingAllowOriginHeader);
|
||||
chan->GetURI(getter_AddRefs(mURI));
|
||||
chan->GetOriginalURI(getter_AddRefs(mOriginalURI));
|
||||
|
||||
mChannelCreationTime = PR_Now();
|
||||
mChannelCreationTimestamp = TimeStamp::Now();
|
||||
mAsyncOpenTime = TimeStamp::Now();
|
||||
|
||||
nsCOMPtr<nsITimedChannel> timedChanel(do_QueryInterface(chan));
|
||||
if (timedChanel) {
|
||||
timedChanel->GetInitiatorType(mInitiatorType);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
NullHttpChannel::Init(nsIURI *aURI,
|
||||
uint32_t aCaps,
|
||||
nsProxyInfo *aProxyInfo,
|
||||
uint32_t aProxyResolveFlags,
|
||||
nsIURI *aProxyURI)
|
||||
{
|
||||
mURI = aURI;
|
||||
mOriginalURI = aURI;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// NullHttpChannel::nsIHttpChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetChannelId(nsACString& aChannelId)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetChannelId(const nsACString& aChannelId)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetTopLevelContentWindowId(uint64_t *aWindowId)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetTopLevelContentWindowId(uint64_t aWindowId)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetTransferSize(uint64_t *aTransferSize)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetDecodedBodySize(uint64_t *aDecodedBodySize)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetRequestMethod(nsACString & aRequestMethod)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetRequestMethod(const nsACString & aRequestMethod)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetReferrer(nsIURI * *aReferrer)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetReferrer(nsIURI *aReferrer)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetReferrerPolicy(uint32_t *aReferrerPolicy)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetReferrerWithPolicy(nsIURI *referrer, uint32_t referrerPolicy)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetRequestHeader(const nsACString & aHeader, nsACString & _retval)
|
||||
{
|
||||
_retval.Truncate();
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetRequestHeader(const nsACString & aHeader, const nsACString & aValue, bool aMerge)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetEmptyRequestHeader(const nsACString & aHeader)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *aVisitor)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::VisitNonDefaultRequestHeaders(nsIHttpHeaderVisitor *aVisitor)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetAllowPipelining(bool *aAllowPipelining)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetAllowPipelining(bool aAllowPipelining)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetAllowSTS(bool *aAllowSTS)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetAllowSTS(bool aAllowSTS)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetRedirectionLimit(uint32_t *aRedirectionLimit)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetRedirectionLimit(uint32_t aRedirectionLimit)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetResponseStatus(uint32_t *aResponseStatus)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetResponseStatusText(nsACString & aResponseStatusText)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetRequestSucceeded(bool *aRequestSucceeded)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetResponseHeader(const nsACString & header, nsACString & _retval)
|
||||
{
|
||||
_retval.Truncate();
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetResponseHeader(const nsACString & header, const nsACString & value, bool merge)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::VisitResponseHeaders(nsIHttpHeaderVisitor *aVisitor)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetOriginalResponseHeader(const nsACString & header,
|
||||
nsIHttpHeaderVisitor *aVisitor)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::VisitOriginalResponseHeaders(nsIHttpHeaderVisitor *aVisitor)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::IsNoStoreResponse(bool *_retval)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::IsNoCacheResponse(bool *_retval)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::IsPrivateResponse(bool *_retval)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::RedirectTo(nsIURI *aNewURI)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetRequestContextID(nsID *_retval)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetRequestContextID(const nsID rcID)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetProtocolVersion(nsACString& aProtocolVersion)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetEncodedBodySize(uint64_t *aEncodedBodySize)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// NullHttpChannel::nsIChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetOriginalURI(nsIURI * *aOriginalURI)
|
||||
{
|
||||
NS_IF_ADDREF(*aOriginalURI = mOriginalURI);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetOriginalURI(nsIURI *aOriginalURI)
|
||||
{
|
||||
mOriginalURI = aOriginalURI;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetURI(nsIURI * *aURI)
|
||||
{
|
||||
NS_IF_ADDREF(*aURI = mURI);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetOwner(nsISupports * *aOwner)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetOwner(nsISupports *aOwner)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetNotificationCallbacks(nsIInterfaceRequestor * *aNotificationCallbacks)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aNotificationCallbacks)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetContentType(nsACString & aContentType)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetContentType(const nsACString & aContentType)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetContentCharset(nsACString & aContentCharset)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetContentCharset(const nsACString & aContentCharset)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetContentLength(int64_t *aContentLength)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetContentLength(int64_t aContentLength)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::Open(nsIInputStream * *_retval)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::Open2(nsIInputStream** aStream)
|
||||
{
|
||||
nsCOMPtr<nsIStreamListener> listener;
|
||||
nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return Open(aStream);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::AsyncOpen2(nsIStreamListener *aListener)
|
||||
{
|
||||
nsCOMPtr<nsIStreamListener> listener = aListener;
|
||||
nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return AsyncOpen(listener, nullptr);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetContentDisposition(uint32_t *aContentDisposition)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetContentDisposition(uint32_t aContentDisposition)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetContentDispositionFilename(nsAString & aContentDispositionFilename)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetContentDispositionFilename(const nsAString & aContentDispositionFilename)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetContentDispositionHeader(nsACString & aContentDispositionHeader)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetLoadInfo(nsILoadInfo * *aLoadInfo)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetLoadInfo(nsILoadInfo *aLoadInfo)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// NullHttpChannel::nsIRequest
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetName(nsACString & aName)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::IsPending(bool *_retval)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetStatus(nsresult *aStatus)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::Cancel(nsresult aStatus)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::Suspend()
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::Resume()
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetLoadGroup(nsILoadGroup * *aLoadGroup)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// NullHttpChannel::nsITimedChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetTimingEnabled(bool *aTimingEnabled)
|
||||
{
|
||||
*aTimingEnabled = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetTimingEnabled(bool aTimingEnabled)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetRedirectCount(uint16_t *aRedirectCount)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetRedirectCount(uint16_t aRedirectCount)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetChannelCreation(mozilla::TimeStamp *aChannelCreation)
|
||||
{
|
||||
*aChannelCreation = mChannelCreationTimestamp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetAsyncOpen(mozilla::TimeStamp *aAsyncOpen)
|
||||
{
|
||||
*aAsyncOpen = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetDomainLookupStart(mozilla::TimeStamp *aDomainLookupStart)
|
||||
{
|
||||
*aDomainLookupStart = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetDomainLookupEnd(mozilla::TimeStamp *aDomainLookupEnd)
|
||||
{
|
||||
*aDomainLookupEnd = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetConnectStart(mozilla::TimeStamp *aConnectStart)
|
||||
{
|
||||
*aConnectStart = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetConnectEnd(mozilla::TimeStamp *aConnectEnd)
|
||||
{
|
||||
*aConnectEnd = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetRequestStart(mozilla::TimeStamp *aRequestStart)
|
||||
{
|
||||
*aRequestStart = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetResponseStart(mozilla::TimeStamp *aResponseStart)
|
||||
{
|
||||
*aResponseStart = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetResponseEnd(mozilla::TimeStamp *aResponseEnd)
|
||||
{
|
||||
*aResponseEnd = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetRedirectStart(mozilla::TimeStamp *aRedirectStart)
|
||||
{
|
||||
*aRedirectStart = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetRedirectStart(mozilla::TimeStamp aRedirectStart)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetRedirectEnd(mozilla::TimeStamp *aRedirectEnd)
|
||||
{
|
||||
*aRedirectEnd = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetRedirectEnd(mozilla::TimeStamp aRedirectEnd)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetInitiatorType(nsAString & aInitiatorType)
|
||||
{
|
||||
aInitiatorType = mInitiatorType;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetInitiatorType(const nsAString & aInitiatorType)
|
||||
{
|
||||
mInitiatorType = aInitiatorType;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetAllRedirectsSameOrigin(bool *aAllRedirectsSameOrigin)
|
||||
{
|
||||
*aAllRedirectsSameOrigin = mAllRedirectsSameOrigin;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetAllRedirectsSameOrigin(bool aAllRedirectsSameOrigin)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetAllRedirectsPassTimingAllowCheck(bool *aAllRedirectsPassTimingAllowCheck)
|
||||
{
|
||||
*aAllRedirectsPassTimingAllowCheck = mAllRedirectsPassTimingAllowCheck;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetAllRedirectsPassTimingAllowCheck(bool aAllRedirectsPassTimingAllowCheck)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::TimingAllowCheck(nsIPrincipal *aOrigin, bool *_retval)
|
||||
{
|
||||
if (!mResourcePrincipal || !aOrigin) {
|
||||
*_retval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool sameOrigin = false;
|
||||
nsresult rv = mResourcePrincipal->Equals(aOrigin, &sameOrigin);
|
||||
if (NS_SUCCEEDED(rv) && sameOrigin) {
|
||||
*_retval = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mTimingAllowOriginHeader == "*") {
|
||||
*_retval = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString origin;
|
||||
nsContentUtils::GetASCIIOrigin(aOrigin, origin);
|
||||
|
||||
if (mTimingAllowOriginHeader == origin) {
|
||||
*_retval = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*_retval = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetCacheReadStart(mozilla::TimeStamp *aCacheReadStart)
|
||||
{
|
||||
*aCacheReadStart = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetCacheReadEnd(mozilla::TimeStamp *aCacheReadEnd)
|
||||
{
|
||||
*aCacheReadEnd = mAsyncOpenTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::GetIsMainDocumentChannel(bool* aValue)
|
||||
{
|
||||
*aValue = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetIsMainDocumentChannel(bool aValue)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
#define IMPL_TIMING_ATTR(name) \
|
||||
NS_IMETHODIMP \
|
||||
NullHttpChannel::Get##name##Time(PRTime* _retval) { \
|
||||
TimeStamp stamp; \
|
||||
Get##name(&stamp); \
|
||||
if (stamp.IsNull()) { \
|
||||
*_retval = 0; \
|
||||
return NS_OK; \
|
||||
} \
|
||||
*_retval = mChannelCreationTime + \
|
||||
(PRTime) ((stamp - mChannelCreationTimestamp).ToSeconds() * 1e6); \
|
||||
return NS_OK; \
|
||||
}
|
||||
|
||||
IMPL_TIMING_ATTR(ChannelCreation)
|
||||
IMPL_TIMING_ATTR(AsyncOpen)
|
||||
IMPL_TIMING_ATTR(DomainLookupStart)
|
||||
IMPL_TIMING_ATTR(DomainLookupEnd)
|
||||
IMPL_TIMING_ATTR(ConnectStart)
|
||||
IMPL_TIMING_ATTR(ConnectEnd)
|
||||
IMPL_TIMING_ATTR(RequestStart)
|
||||
IMPL_TIMING_ATTR(ResponseStart)
|
||||
IMPL_TIMING_ATTR(ResponseEnd)
|
||||
IMPL_TIMING_ATTR(CacheReadStart)
|
||||
IMPL_TIMING_ATTR(CacheReadEnd)
|
||||
IMPL_TIMING_ATTR(RedirectStart)
|
||||
IMPL_TIMING_ATTR(RedirectEnd)
|
||||
|
||||
#undef IMPL_TIMING_ATTR
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
66
netwerk/protocol/http/NullHttpChannel.h
Normal file
66
netwerk/protocol/http/NullHttpChannel.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/* 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_NullHttpChannel_h
|
||||
#define mozilla_net_NullHttpChannel_h
|
||||
|
||||
#include "nsINullChannel.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsITimedChannel.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsString.h"
|
||||
#include "prtime.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class nsProxyInfo;
|
||||
|
||||
class NullHttpChannel final
|
||||
: public nsINullChannel
|
||||
, public nsIHttpChannel
|
||||
, public nsITimedChannel
|
||||
{
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSINULLCHANNEL
|
||||
NS_DECL_NSIHTTPCHANNEL
|
||||
NS_DECL_NSITIMEDCHANNEL
|
||||
NS_DECL_NSIREQUEST
|
||||
NS_DECL_NSICHANNEL
|
||||
|
||||
NullHttpChannel();
|
||||
|
||||
// Copies the URI, Principal and Timing-Allow-Origin headers from the
|
||||
// passed channel to this object, to be used for resource timing checks
|
||||
explicit NullHttpChannel(nsIHttpChannel * chan);
|
||||
|
||||
// Same signature as nsHttpChannel::Init
|
||||
nsresult Init(nsIURI *aURI, uint32_t aCaps, nsProxyInfo *aProxyInfo,
|
||||
uint32_t aProxyResolveFlags,
|
||||
nsIURI *aProxyURI);
|
||||
private:
|
||||
~NullHttpChannel() { }
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
nsCOMPtr<nsIURI> mOriginalURI;
|
||||
|
||||
nsString mInitiatorType;
|
||||
PRTime mChannelCreationTime;
|
||||
TimeStamp mAsyncOpenTime;
|
||||
TimeStamp mChannelCreationTimestamp;
|
||||
nsCOMPtr<nsIPrincipal> mResourcePrincipal;
|
||||
nsCString mTimingAllowOriginHeader;
|
||||
bool mAllRedirectsSameOrigin;
|
||||
bool mAllRedirectsPassTimingAllowCheck;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
|
||||
#endif // mozilla_net_NullHttpChannel_h
|
||||
333
netwerk/protocol/http/NullHttpTransaction.cpp
Normal file
333
netwerk/protocol/http/NullHttpTransaction.cpp
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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/. */
|
||||
|
||||
// HttpLog.h should generally be included first
|
||||
#include "HttpLog.h"
|
||||
|
||||
#include "nsHttp.h"
|
||||
#include "NullHttpTransaction.h"
|
||||
#include "nsHttpHandler.h"
|
||||
#include "nsHttpRequestHead.h"
|
||||
#include "nsIHttpActivityObserver.h"
|
||||
#include "NullHttpChannel.h"
|
||||
#include "nsQueryObject.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class CallObserveActivity final : public nsIRunnable
|
||||
{
|
||||
~CallObserveActivity()
|
||||
{
|
||||
}
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
CallObserveActivity(nsIHttpActivityObserver *aActivityDistributor,
|
||||
const nsCString &aHost,
|
||||
int32_t aPort,
|
||||
bool aEndToEndSSL,
|
||||
uint32_t aActivityType,
|
||||
uint32_t aActivitySubtype,
|
||||
PRTime aTimestamp,
|
||||
uint64_t aExtraSizeData,
|
||||
const nsACString &aExtraStringData)
|
||||
: mActivityDistributor(aActivityDistributor)
|
||||
, mHost(aHost)
|
||||
, mPort(aPort)
|
||||
, mEndToEndSSL(aEndToEndSSL)
|
||||
, mActivityType(aActivityType)
|
||||
, mActivitySubtype(aActivitySubtype)
|
||||
, mTimestamp(aTimestamp)
|
||||
, mExtraSizeData(aExtraSizeData)
|
||||
, mExtraStringData(aExtraStringData)
|
||||
{
|
||||
}
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsAutoCString port(NS_LITERAL_CSTRING(""));
|
||||
if (mPort != -1 && ((mEndToEndSSL && mPort != 443) ||
|
||||
(!mEndToEndSSL && mPort != 80))) {
|
||||
port.AppendInt(mPort);
|
||||
}
|
||||
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri),
|
||||
(mEndToEndSSL ? NS_LITERAL_CSTRING("https://")
|
||||
: NS_LITERAL_CSTRING("http://") ) + mHost + port);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<NullHttpChannel> channel = new NullHttpChannel();
|
||||
channel->Init(uri, 0, nullptr, 0, nullptr);
|
||||
mActivityDistributor->ObserveActivity(
|
||||
nsCOMPtr<nsISupports>(do_QueryObject(channel)),
|
||||
mActivityType,
|
||||
mActivitySubtype,
|
||||
mTimestamp,
|
||||
mExtraSizeData,
|
||||
mExtraStringData);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsCOMPtr<nsIHttpActivityObserver> mActivityDistributor;
|
||||
nsCString mHost;
|
||||
int32_t mPort;
|
||||
bool mEndToEndSSL;
|
||||
uint32_t mActivityType;
|
||||
uint32_t mActivitySubtype;
|
||||
PRTime mTimestamp;
|
||||
uint64_t mExtraSizeData;
|
||||
nsCString mExtraStringData;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(CallObserveActivity, nsIRunnable)
|
||||
|
||||
NS_IMPL_ISUPPORTS(NullHttpTransaction, NullHttpTransaction, nsISupportsWeakReference)
|
||||
|
||||
NullHttpTransaction::NullHttpTransaction(nsHttpConnectionInfo *ci,
|
||||
nsIInterfaceRequestor *callbacks,
|
||||
uint32_t caps)
|
||||
: mStatus(NS_OK)
|
||||
, mCaps(caps | NS_HTTP_ALLOW_KEEPALIVE)
|
||||
, mRequestHead(nullptr)
|
||||
, mCapsToClear(0)
|
||||
, mIsDone(false)
|
||||
, mClaimed(false)
|
||||
, mCallbacks(callbacks)
|
||||
, mConnectionInfo(ci)
|
||||
{
|
||||
nsresult rv;
|
||||
mActivityDistributor = do_GetService(NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID,
|
||||
&rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool activityDistributorActive;
|
||||
rv = mActivityDistributor->GetIsActive(&activityDistributorActive);
|
||||
if (NS_SUCCEEDED(rv) && activityDistributorActive) {
|
||||
// There are some observers registered at activity distributor.
|
||||
LOG(("NulHttpTransaction::NullHttpTransaction() "
|
||||
"mActivityDistributor is active "
|
||||
"[this=%p, %s]", this, ci->GetOrigin().get()));
|
||||
} else {
|
||||
// There is no observer, so don't use it.
|
||||
mActivityDistributor = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NullHttpTransaction::~NullHttpTransaction()
|
||||
{
|
||||
mCallbacks = nullptr;
|
||||
delete mRequestHead;
|
||||
}
|
||||
|
||||
bool
|
||||
NullHttpTransaction::Claim()
|
||||
{
|
||||
if (mClaimed) {
|
||||
return false;
|
||||
}
|
||||
mClaimed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
NullHttpTransaction::SetConnection(nsAHttpConnection *conn)
|
||||
{
|
||||
mConnection = conn;
|
||||
}
|
||||
|
||||
nsAHttpConnection *
|
||||
NullHttpTransaction::Connection()
|
||||
{
|
||||
return mConnection.get();
|
||||
}
|
||||
|
||||
void
|
||||
NullHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **outCB)
|
||||
{
|
||||
nsCOMPtr<nsIInterfaceRequestor> copyCB(mCallbacks);
|
||||
*outCB = copyCB.forget().take();
|
||||
}
|
||||
|
||||
void
|
||||
NullHttpTransaction::OnTransportStatus(nsITransport* transport,
|
||||
nsresult status, int64_t progress)
|
||||
{
|
||||
if (mActivityDistributor) {
|
||||
NS_DispatchToMainThread(new CallObserveActivity(mActivityDistributor,
|
||||
mConnectionInfo->GetOrigin(),
|
||||
mConnectionInfo->OriginPort(),
|
||||
mConnectionInfo->EndToEndSSL(),
|
||||
NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT,
|
||||
static_cast<uint32_t>(status),
|
||||
PR_Now(),
|
||||
progress,
|
||||
EmptyCString()));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
NullHttpTransaction::IsDone()
|
||||
{
|
||||
return mIsDone;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NullHttpTransaction::Status()
|
||||
{
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
NullHttpTransaction::Caps()
|
||||
{
|
||||
return mCaps & ~mCapsToClear;
|
||||
}
|
||||
|
||||
void
|
||||
NullHttpTransaction::SetDNSWasRefreshed()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "SetDNSWasRefreshed on main thread only!");
|
||||
mCapsToClear |= NS_HTTP_REFRESH_DNS;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
NullHttpTransaction::Available()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NullHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader,
|
||||
uint32_t count, uint32_t *countRead)
|
||||
{
|
||||
*countRead = 0;
|
||||
mIsDone = true;
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NullHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
|
||||
uint32_t count, uint32_t *countWritten)
|
||||
{
|
||||
*countWritten = 0;
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
NullHttpTransaction::Http1xTransactionCount()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsHttpRequestHead *
|
||||
NullHttpTransaction::RequestHead()
|
||||
{
|
||||
// We suport a requesthead at all so that a CONNECT tunnel transaction
|
||||
// can obtain a Host header from it, but we lazy-popualate that header.
|
||||
|
||||
if (!mRequestHead) {
|
||||
mRequestHead = new nsHttpRequestHead();
|
||||
|
||||
nsAutoCString hostHeader;
|
||||
nsCString host(mConnectionInfo->GetOrigin());
|
||||
nsresult rv = nsHttpHandler::GenerateHostPort(host,
|
||||
mConnectionInfo->OriginPort(),
|
||||
hostHeader);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mRequestHead->SetHeader(nsHttp::Host, hostHeader);
|
||||
if (mActivityDistributor) {
|
||||
// Report request headers.
|
||||
nsCString reqHeaderBuf;
|
||||
mRequestHead->Flatten(reqHeaderBuf, false);
|
||||
NS_DispatchToMainThread(new CallObserveActivity(mActivityDistributor,
|
||||
mConnectionInfo->GetOrigin(),
|
||||
mConnectionInfo->OriginPort(),
|
||||
mConnectionInfo->EndToEndSSL(),
|
||||
NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
|
||||
NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_HEADER,
|
||||
PR_Now(), 0, reqHeaderBuf));
|
||||
}
|
||||
}
|
||||
|
||||
// CONNECT tunnels may also want Proxy-Authorization but that is a lot
|
||||
// harder to determine, so for now we will let those connections fail in
|
||||
// the NullHttpTransaction and let them be retried from the pending queue
|
||||
// with a bound transaction
|
||||
}
|
||||
|
||||
return mRequestHead;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NullHttpTransaction::TakeSubTransactions(
|
||||
nsTArray<RefPtr<nsAHttpTransaction> > &outTransactions)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
void
|
||||
NullHttpTransaction::SetProxyConnectFailed()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
NullHttpTransaction::Close(nsresult reason)
|
||||
{
|
||||
mStatus = reason;
|
||||
mConnection = nullptr;
|
||||
mIsDone = true;
|
||||
if (mActivityDistributor) {
|
||||
// Report that this transaction is closing.
|
||||
NS_DispatchToMainThread(new CallObserveActivity(mActivityDistributor,
|
||||
mConnectionInfo->GetOrigin(),
|
||||
mConnectionInfo->OriginPort(),
|
||||
mConnectionInfo->EndToEndSSL(),
|
||||
NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
|
||||
NS_HTTP_ACTIVITY_SUBTYPE_TRANSACTION_CLOSE,
|
||||
PR_Now(), 0, EmptyCString()));
|
||||
}
|
||||
}
|
||||
|
||||
nsHttpConnectionInfo *
|
||||
NullHttpTransaction::ConnectionInfo()
|
||||
{
|
||||
return mConnectionInfo;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NullHttpTransaction::AddTransaction(nsAHttpTransaction *trans)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
NullHttpTransaction::PipelineDepth()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsresult
|
||||
NullHttpTransaction::SetPipelinePosition(int32_t position)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
NullHttpTransaction::PipelinePosition()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue