import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View 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']

View 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;
}
////////////////////////////////////////////////////////////////////////////////

View 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__

View 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=&amp;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("&amp;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("&amp;context=");
char* escapedContext = nsEscapeHTML(mContextString.get());
url += escapedContext;
free(escapedContext);
url.AppendLiteral("&amp;eid=");
char* escapedEID = nsEscapeHTML(aIdEnhance.BeginReading());
url += escapedEID;
free(escapedEID);
nsAutoCString cacheUriSpec;
aURI->GetAsciiSpec(cacheUriSpec);
char* escapedCacheURI = nsEscapeHTML(cacheUriSpec.get());
url.AppendLiteral("&amp;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("&nbsp;"));
}
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;
}
////////////////////////////////////////////////////////////////////////////////

View 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__

View 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("&lt;");
break;
case '>':
result.AppendLiteral("&gt;");
break;
case '&':
result.AppendLiteral("&amp;");
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;
}

View 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__

View 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

View 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___ */

View 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

View 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)
%}

View 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

View 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 */

View 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

View 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 */

View 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',
]

View 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;
}

View 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___ */

View 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;
}

View 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___ */

View 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)

View 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();
}

View 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

View 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

View 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

View 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_

View 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/',
]

View 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

View 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);
}

View 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

View 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

View 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

View 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']

View 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;
}

View 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__

View 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);
}

View 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__

View 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;
};

View 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);
};

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,4 @@
Test
here
there
everywhere

View 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___

View 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']

View 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();
}

View 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___ */

File diff suppressed because it is too large Load diff

View 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_

View 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;
}

View 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

View 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();
}

View 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__

View 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);
};

View 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);
};

View file

@ -0,0 +1,5 @@
<html>
<body>
<h2>Click a link to the left</h2>
</body>
</html>

View 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>

View 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>

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load diff

View 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

View 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

View 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

View 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

View 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

View 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__

File diff suppressed because it is too large Load diff

View 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

File diff suppressed because it is too large Load diff

View 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

View 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

View 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

File diff suppressed because it is too large Load diff

View 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

File diff suppressed because it is too large Load diff

View 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

File diff suppressed because it is too large Load diff

View 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

File diff suppressed because it is too large Load diff

View 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

File diff suppressed because it is too large Load diff

View 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

View 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

View 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

View 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);
}

View 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__

View 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__

View 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

View 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

View 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

View 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

View 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