Bug 1337791 - Part 3: http/2 origin frame extension test. r=nwgh

This commit is contained in:
Patrick McManus 2017-04-03 17:24:49 -04:00
Родитель 06bd9a4bae
Коммит 0ef4fc0939
8 изменённых файлов: 325 добавлений и 109 удалений

Просмотреть файл

@ -0,0 +1,254 @@
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/NetUtil.jsm");
var h2Port;
var prefs;
var spdypref;
var http2pref;
function run_test() {
var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
h2Port = env.get("MOZHTTP2_PORT");
do_check_neq(h2Port, null);
do_check_neq(h2Port, "");
// Set to allow the cert presented by our H2 server
do_get_profile();
prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
spdypref = prefs.getBoolPref("network.http.spdy.enabled");
http2pref = prefs.getBoolPref("network.http.spdy.enabled.http2");
prefs.setBoolPref("network.http.spdy.enabled", true);
prefs.setBoolPref("network.http.spdy.enabled.http2", true);
prefs.setCharPref("network.dns.localDomains", "foo.example.com, alt1.example.com");
// The moz-http2 cert is for {foo, alt1, alt2}.example.com and is signed by CA.cert.der
// so add that cert to the trust list as a signing cert.
let certdb = Cc["@mozilla.org/security/x509certdb;1"]
.getService(Ci.nsIX509CertDB);
addCertFromFile(certdb, "CA.cert.der", "CTu,u,u");
doTest1();
}
function resetPrefs() {
prefs.setBoolPref("network.http.spdy.enabled", spdypref);
prefs.setBoolPref("network.http.spdy.enabled.http2", http2pref);
prefs.clearUserPref("network.dns.localDomains");
}
function readFile(file) {
let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
.createInstance(Ci.nsIFileInputStream);
fstream.init(file, -1, 0, 0);
let data = NetUtil.readInputStreamToString(fstream, fstream.available());
fstream.close();
return data;
}
function addCertFromFile(certdb, filename, trustString) {
let certFile = do_get_file(filename, false);
let der = readFile(certFile);
certdb.addCert(der, trustString);
}
function makeChan(origin) {
return NetUtil.newChannel({
uri: origin,
loadUsingSystemPrincipal: true
}).QueryInterface(Ci.nsIHttpChannel);
}
var nextTest;
var nextPortExpectedToBeSame = false;
var currentPort = 0;
var forceReload = false;
var forceFailListener = false;
var Listener = function() {};
Listener.prototype.clientPort = 0;
Listener.prototype = {
onStartRequest: function testOnStartRequest(request, ctx) {
do_check_true(request instanceof Components.interfaces.nsIHttpChannel);
if (!Components.isSuccessCode(request.status)) {
do_throw("Channel should have a success code! (" + request.status + ")");
}
do_check_eq(request.responseStatus, 200);
this.clientPort = parseInt(request.getResponseHeader("x-client-port"));
},
onDataAvailable: function testOnDataAvailable(request, ctx, stream, off, cnt) {
read_stream(stream, cnt);
},
onStopRequest: function testOnStopRequest(request, ctx, status) {
do_check_true(Components.isSuccessCode(status));
if (nextPortExpectedToBeSame) {
do_check_eq(currentPort, this.clientPort);
} else {
do_check_neq(currentPort, this.clientPort);
}
currentPort = this.clientPort;
nextTest();
do_test_finished();
}
};
var FailListener = function() {};
FailListener.prototype = {
onStartRequest: function testOnStartRequest(request, ctx) {
do_check_true(request instanceof Components.interfaces.nsIHttpChannel);
do_check_false(Components.isSuccessCode(request.status));
},
onDataAvailable: function testOnDataAvailable(request, ctx, stream, off, cnt) {
read_stream(stream, cnt);
},
onStopRequest: function testOnStopRequest(request, ctx, status) {
do_check_false(Components.isSuccessCode(request.status));
nextTest();
do_test_finished();
}
};
function testsDone()
{
dump("testsDone\n");
resetPrefs();
}
function doTest()
{
dump("execute doTest " + origin + "\n");
var chan = makeChan(origin);
var listener;
if (!forceFailListener) {
listener = new Listener();
} else {
listener = new FailListener();
}
forceFailListener = false;
if (!forceReload) {
chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
} else {
chan.loadFlags = Ci.nsIRequest.LOAD_FRESH_CONNECTION |
Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
}
forceReload = false;
chan.asyncOpen2(listener);
}
function doTest1()
{
dump("doTest1()\n");
origin = "https://foo.example.com:" + h2Port + "/origin-1";
nextTest = doTest2;
nextPortExpectedToBeSame = false;
do_test_pending();
doTest();
}
function doTest2()
{
// plain connection reuse
dump("doTest2()\n");
origin = "https://foo.example.com:" + h2Port + "/origin-2";
nextTest = doTest3;
nextPortExpectedToBeSame = true;
do_test_pending();
doTest();
}
function doTest3()
{
// 7540 style coalescing
dump("doTest3()\n");
origin = "https://alt1.example.com:" + h2Port + "/origin-3";
nextTest = doTest4;
nextPortExpectedToBeSame = true;
do_test_pending();
doTest();
}
function doTest4()
{
// forces an empty origin frame to be omitted
dump("doTest4()\n");
origin = "https://foo.example.com:" + h2Port + "/origin-4";
nextTest = doTest5;
nextPortExpectedToBeSame = true;
do_test_pending();
doTest();
}
function doTest5()
{
// 7540 style coalescing should not work due to empty origin set
dump("doTest5()\n");
origin = "https://alt1.example.com:" + h2Port + "/origin-5";
nextTest = doTest6;
nextPortExpectedToBeSame = false;
do_test_pending();
doTest();
}
function doTest6()
{
// get a fresh connection with alt1 and alt2 in origin set
// note that there is no dns for alt2
dump("doTest6()\n");
origin = "https://foo.example.com:" + h2Port + "/origin-6";
nextTest = doTest7;
nextPortExpectedToBeSame = false;
forceReload = true;
do_test_pending();
doTest();
}
function doTest7()
{
// check conn reuse to ensure sni is implicit in origin set
dump("doTest7()\n");
origin = "https://foo.example.com:" + h2Port + "/origin-7";
nextTest = doTest8;
nextPortExpectedToBeSame = true;
do_test_pending();
doTest();
}
function doTest8()
{
// alt1 is in origin set (and is 7540 eligible)
dump("doTest8()\n");
origin = "https://alt1.example.com:" + h2Port + "/origin-8";
nextTest = doTest9;
nextPortExpectedToBeSame = true;
do_test_pending();
doTest();
}
function doTest9()
{
// alt2 is in origin set but does not have dns
dump("doTest9()\n");
origin = "https://alt2.example.com:" + h2Port + "/origin-9";
nextTest = doTest10;
nextPortExpectedToBeSame = true;
do_test_pending();
doTest();
}
function doTest10()
{
// bar is in origin set but does not have dns like alt2
// but the cert is not valid for bar. so expect a failure
dump("doTest10()\n");
origin = "https://bar.example.com:" + h2Port + "/origin-10";
nextTest = testsDone;
nextPortExpectedToBeSame = false;
forceFailListener = true;
do_test_pending();
doTest();
}

Просмотреть файл

@ -250,6 +250,7 @@ run-sequentially = node server exceptions dont replay well
skip-if = os == "win"
[test_nojsredir.js]
[test_offline_status.js]
[test_origin.js]
[test_original_sent_received_head.js]
[test_parse_content_type.js]
[test_permmgr.js]

Просмотреть файл

@ -1,79 +1,24 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=Maine, O=CA Example
Validity
Not Before: Apr 29 05:29:19 2015 GMT
Not After : Apr 26 05:29:19 2025 GMT
Subject: C=US, ST=Maine, O=Example Com, CN=foo.example.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:cf:ff:c0:27:3b:a3:11:b5:7f:5d:4f:22:f9:75:
48:47:d9:3a:ce:9b:66:82:4e:e4:ae:ab:78:d3:4c:
3a:9a:5c:37:97:b2:7b:4e:2a:54:77:16:2a:3e:6f:
52:ee:4b:49:46:1d:6b:18:9a:ed:b1:ad:64:9f:8b:
e5:fa:e4:60:7b:39:0e:db:e8:b4:2d:4b:e8:ab:37:
e8:90:ec:eb:0f:3e:6b:40:7a:d1:da:e6:68:b3:f4:
f6:68:54:5b:27:90:6d:c2:c3:04:de:85:23:2b:3c:
66:4e:06:79:58:93:a1:71:d7:ec:74:55:a4:84:9d:
41:22:2a:7a:76:ae:56:b1:6f:15:2d:f2:f5:9c:64:
3e:4f:0f:6e:8f:b6:28:66:e9:89:04:5d:1d:21:77:
f8:03:d3:89:ed:7c:f4:3b:42:02:c8:8d:de:47:74:
1f:4a:5d:fe:8d:d1:57:37:08:54:bf:89:d8:f7:27:
22:a7:2a:5d:aa:d5:b0:61:22:9b:96:75:ee:ab:09:
ca:a9:cb:2b:1e:88:7c:5a:53:7e:5f:88:c4:43:ea:
e8:a7:db:35:6c:b2:89:ad:98:e0:96:c9:83:c4:c1:
e7:2a:5c:f8:99:5c:9e:01:9c:e6:99:bd:18:5c:69:
d4:10:f1:46:88:37:0b:4e:76:5f:6a:1a:21:c2:a4:
16:d1
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
76:BC:13:90:F7:85:1B:1C:24:A1:CC:65:8A:4F:4C:0C:7F:10:D3:F5
X509v3 Authority Key Identifier:
keyid:F7:FC:76:AF:C5:1A:E9:C9:42:6C:38:DF:8B:07:9E:2B:2C:E5:8E:20
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Key Encipherment
Signature Algorithm: sha256WithRSAEncryption
03:ab:2a:9e:e5:cd:5c:88:5a:6c:f7:4b:7a:7c:ef:85:2c:31:
df:03:79:31:a6:c5:c8:2b:c6:21:a5:33:2b:a0:4b:e2:7e:0a:
86:9b:72:25:b6:75:43:41:7c:30:9f:15:b4:9f:34:50:57:eb:
87:f9:1e:9f:b6:cd:81:36:92:61:66:d5:fe:e2:c5:ed:de:f1:
ce:85:0b:f9:6a:2b:32:4d:29:f1:a9:94:57:a3:0f:74:93:12:
c9:0a:28:5e:72:9f:4f:0f:78:f5:84:11:5a:9f:d7:1c:4c:fd:
13:d8:3d:4c:f8:dd:4c:c6:1c:fd:63:ee:f5:d5:96:f5:00:2c:
e6:bb:c9:4c:d8:6a:19:59:58:2b:d4:05:ab:57:47:1c:49:d6:
c5:56:1a:e3:64:10:19:9b:44:3e:74:8b:19:73:28:86:96:b4:
d1:2a:49:23:07:25:97:64:8f:1b:1c:64:76:12:e0:df:e3:cf:
55:d5:7c:e9:77:d4:69:2f:c7:9a:fd:ce:1a:29:ab:d7:88:68:
93:de:75:e4:d6:85:29:e2:b6:b7:59:20:e3:b5:20:b7:e8:0b:
23:9b:4c:b4:e8:d9:90:cf:e9:2f:9e:a8:22:a2:ef:6a:68:65:
f6:c4:81:ed:75:77:88:01:f2:47:03:1a:de:1f:44:38:47:fa:
aa:69:f2:98
-----BEGIN CERTIFICATE-----
MIIDVDCCAjygAwIBAgIBATANBgkqhkiG9w0BAQsFADAyMQswCQYDVQQGEwJVUzEO
MAwGA1UECAwFTWFpbmUxEzARBgNVBAoMCkNBIEV4YW1wbGUwHhcNMTUwNDI5MDUy
OTE5WhcNMjUwNDI2MDUyOTE5WjBNMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFTWFp
bmUxFDASBgNVBAoMC0V4YW1wbGUgQ29tMRgwFgYDVQQDDA9mb28uZXhhbXBsZS5j
b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP/8AnO6MRtX9dTyL5
dUhH2TrOm2aCTuSuq3jTTDqaXDeXsntOKlR3Fio+b1LuS0lGHWsYmu2xrWSfi+X6
5GB7OQ7b6LQtS+irN+iQ7OsPPmtAetHa5miz9PZoVFsnkG3CwwTehSMrPGZOBnlY
k6Fx1+x0VaSEnUEiKnp2rlaxbxUt8vWcZD5PD26Ptihm6YkEXR0hd/gD04ntfPQ7
QgLIjd5HdB9KXf6N0Vc3CFS/idj3JyKnKl2q1bBhIpuWde6rCcqpyyseiHxaU35f
iMRD6uin2zVssomtmOCWyYPEwecqXPiZXJ4BnOaZvRhcadQQ8UaINwtOdl9qGiHC
pBbRAgMBAAGjWjBYMB0GA1UdDgQWBBR2vBOQ94UbHCShzGWKT0wMfxDT9TAfBgNV
HSMEGDAWgBT3/HavxRrpyUJsON+LB54rLOWOIDAJBgNVHRMEAjAAMAsGA1UdDwQE
AwIFoDANBgkqhkiG9w0BAQsFAAOCAQEAA6sqnuXNXIhabPdLenzvhSwx3wN5MabF
yCvGIaUzK6BL4n4KhptyJbZ1Q0F8MJ8VtJ80UFfrh/ken7bNgTaSYWbV/uLF7d7x
zoUL+WorMk0p8amUV6MPdJMSyQooXnKfTw949YQRWp/XHEz9E9g9TPjdTMYc/WPu
9dWW9QAs5rvJTNhqGVlYK9QFq1dHHEnWxVYa42QQGZtEPnSLGXMohpa00SpJIwcl
l2SPGxxkdhLg3+PPVdV86XfUaS/Hmv3OGimr14hok9515NaFKeK2t1kg47Ugt+gL
I5tMtOjZkM/pL56oIqLvamhl9sSB7XV3iAHyRwMa3h9EOEf6qmnymA==
MIID+TCCAuGgAwIBAgIJAKu6XZkGFQ8NMA0GCSqGSIb3DQEBCwUAMDIxCzAJBgNV
BAYTAlVTMQ4wDAYDVQQIDAVNYWluZTETMBEGA1UECgwKQ0EgRXhhbXBsZTAeFw0x
NzAzMjgwMjIxMjZaFw0yNzAzMjYwMjIxMjZaMIHIMQswCQYDVQQGEwJVUzERMA8G
A1UECAwITmV3IFlvcmsxEjAQBgNVBAcMCVJvY2hlc3RlcjESMBAGA1UECgwJRW5k
IFBvaW50MRcwFQYDVQQLDA5UZXN0aW5nIERvbWFpbjFLMEkGCSqGSIb3DQEJARY8
eW91ci1hZG1pbmlzdHJhdGl2ZS1hZGRyZXNzQHlvdXItYXdlc29tZS1leGlzdGlu
Zy1kb21haW4uY29tMRgwFgYDVQQDDA9mb28uZXhhbXBsZS5jb20wggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP/8AnO6MRtX9dTyL5dUhH2TrOm2aCTuSu
q3jTTDqaXDeXsntOKlR3Fio+b1LuS0lGHWsYmu2xrWSfi+X65GB7OQ7b6LQtS+ir
N+iQ7OsPPmtAetHa5miz9PZoVFsnkG3CwwTehSMrPGZOBnlYk6Fx1+x0VaSEnUEi
Knp2rlaxbxUt8vWcZD5PD26Ptihm6YkEXR0hd/gD04ntfPQ7QgLIjd5HdB9KXf6N
0Vc3CFS/idj3JyKnKl2q1bBhIpuWde6rCcqpyyseiHxaU35fiMRD6uin2zVssomt
mOCWyYPEwecqXPiZXJ4BnOaZvRhcadQQ8UaINwtOdl9qGiHCpBbRAgMBAAGjezB5
MB8GA1UdIwQYMBaAFPf8dq/FGunJQmw434sHniss5Y4gMAkGA1UdEwQCMAAwCwYD
VR0PBAQDAgTwMD4GA1UdEQQ3MDWCD2Zvby5leGFtcGxlLmNvbYIQYWx0MS5leGFt
cGxlLmNvbYIQYWx0Mi5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEARitg
nxH87RYCMo/wbfSyttkiDmlw1vuEFI6dq9D6iTYcJK9pD9VvQ8e/k0J0FdtnIGBD
O9+kKuPCRIjJt0mRToQHXI4SFIEqUraI5xA5VdXT2FR5KsshNSw6LjV25gvv0hcI
6YBOlJ1IzntSA3h7lGGhgqH2ln32hzTQ8ob8F8i3GecOIk6mDkgCHTPRe7tfyTKw
7c6Z8By6Es84RCQdxXf6AouhJw9SfZl1T5bcy5vDbBcNYenfvueCLezNX6kK7orh
KsqnxWr2cG8c3X1OIuuvAEUbQ78InOb4OPiQQXcfv+dzxnv7tK6pNRcmMUhabwM8
J3i97uzqNXPwTFMu3Q==
-----END CERTIFICATE-----

Просмотреть файл

@ -743,6 +743,21 @@ function handleRequest(req, res) {
}
// default response from here
}
else if (u.pathname === "/origin-4") {
var originList = [ ];
req.stream.connection.originFrame(originList);
res.setHeader("x-client-port", req.remotePort);
}
else if (u.pathname === "/origin-6") {
var originList = [ "https://alt1.example.com:" + serverPort,
"https://alt2.example.com:" + serverPort,
"https://bar.example.com:" + serverPort ];
req.stream.connection.originFrame(originList);
res.setHeader("x-client-port", req.remotePort);
}
else if (u.pathname.substring(0,8) === "/origin-") { // test_origin.js coalescing
res.setHeader("x-client-port", req.remotePort);
}
res.setHeader('Content-Type', 'text/html');
if (req.httpVersionMajor != 2) {

Просмотреть файл

@ -123,7 +123,7 @@ Connection.prototype._initializeStreamManagement = function _initializeStreamMan
Connection.prototype._writeControlFrame = function _writeControlFrame(frame) {
if ((frame.type === 'SETTINGS') || (frame.type === 'PING') ||
(frame.type === 'GOAWAY') || (frame.type === 'WINDOW_UPDATE') ||
(frame.type === 'ALTSVC')) {
(frame.type === 'ALTSVC') || (frame.type == 'ORIGIN')) {
this._log.debug({ frame: frame }, 'Receiving connection level frame');
this.emit(frame.type, frame);
} else {
@ -557,6 +557,17 @@ Connection.prototype._receivePing = function _receivePing(frame) {
}
};
Connection.prototype.originFrame = function originFrame(originList) {
this._log.debug(originList, 'emitting origin frame');
this.push({
type: 'ORIGIN',
flags: {},
stream: 0,
originList : originList,
});
};
// Terminating the connection
Connection.prototype.close = function close(error) {
if (this._closed) {

Просмотреть файл

@ -64,7 +64,6 @@ function Flow(flowControlId) {
this._queue = [];
this._ended = false;
this._received = 0;
this._blocked = false;
}
Flow.prototype = Object.create(Duplex.prototype, { constructor: { value: Flow } });
@ -157,7 +156,6 @@ Flow.prototype._read = function _read() {
// * if there are items in the flow control queue, then let's put them into the output queue (to
// the extent it is possible with respect to the window size and output queue feedback)
else if (this._window > 0) {
this._blocked = false;
this._readableState.sync = true; // to avoid reentrant calls
do {
var moreNeeded = this._push(this._queue[0]);
@ -173,14 +171,8 @@ Flow.prototype._read = function _read() {
}
// * otherwise, come back when the flow control window is positive
else if (!this._blocked) {
this._parentPush({
type: 'BLOCKED',
flags: {},
stream: this._flowControlId
});
else {
this.once('window_update', this._read);
this._blocked = true;
}
};

Просмотреть файл

@ -1069,26 +1069,24 @@ Deserializer.ALTSVC = function readAltSvc(buffer, frame) {
}
};
// BLOCKED
// ------------------------------------------------------------
//
// The BLOCKED frame (type=0xB) indicates that the sender is unable to send data
// due to a closed flow control window.
//
// The BLOCKED frame does not define any flags and contains no payload.
frameTypes[0xB] = 'ORIGIN';
frameFlags.ORIGIN = [];
typeSpecificAttributes.ORIGIN = ['originList'];
frameTypes[0xB] = 'BLOCKED';
frameFlags.BLOCKED = [];
typeSpecificAttributes.BLOCKED = [];
Serializer.BLOCKED = function writeBlocked(frame, buffers) {
Serializer.ORIGIN = function writeOrigin(frame, buffers) {
for (var i = 0; i < frame.originList.length; i++) {
var buffer = new Buffer(2);
buffer.writeUInt16BE(frame.originList[i].length, 0);
buffers.push(buffer);
buffers.push(new Buffer(frame.originList[i], 'ascii'));
}
};
Deserializer.BLOCKED = function readBlocked(buffer, frame) {
Deserializer.ORIGIN = function readOrigin(buffer, frame) {
// ignored
};
// [Error Codes](https://tools.ietf.org/html/rfc7540#section-7)
// ------------------------------------------------------------

Просмотреть файл

@ -253,7 +253,7 @@ Stream.prototype._writeUpstream = function _writeUpstream(frame) {
this._onPriority(frame);
} else if (frame.type === 'ALTSVC') {
// TODO
} else if (frame.type === 'BLOCKED') {
} else if (frame.type === 'ORIGIN') {
// TODO
}
@ -413,7 +413,7 @@ Stream.prototype._transition = function transition(sending, frame) {
var connectionError;
var streamError;
var DATA = false, HEADERS = false, PRIORITY = false, ALTSVC = false, BLOCKED = false;
var DATA = false, HEADERS = false, PRIORITY = false, ALTSVC = false, ORIGIN = false;
var RST_STREAM = false, PUSH_PROMISE = false, WINDOW_UPDATE = false;
switch(frame.type) {
case 'DATA' : DATA = true; break;
@ -423,7 +423,7 @@ Stream.prototype._transition = function transition(sending, frame) {
case 'PUSH_PROMISE' : PUSH_PROMISE = true; break;
case 'WINDOW_UPDATE': WINDOW_UPDATE = true; break;
case 'ALTSVC' : ALTSVC = true; break;
case 'BLOCKED' : BLOCKED = true; break;
case 'ORIGIN' : ORIGIN = true; break;
}
var previousState = this.state;
@ -483,7 +483,7 @@ Stream.prototype._transition = function transition(sending, frame) {
this._setState('CLOSED');
} else if (receiving && HEADERS) {
this._setState('HALF_CLOSED_LOCAL');
} else if (BLOCKED || PRIORITY) {
} else if (PRIORITY || ORIGIN) {
/* No state change */
} else {
connectionError = 'PROTOCOL_ERROR';
@ -518,7 +518,7 @@ Stream.prototype._transition = function transition(sending, frame) {
case 'HALF_CLOSED_LOCAL':
if (RST_STREAM || (receiving && frame.flags.END_STREAM)) {
this._setState('CLOSED');
} else if (BLOCKED || ALTSVC || receiving || PRIORITY || (sending && WINDOW_UPDATE)) {
} else if (ORIGIN || ALTSVC || receiving || PRIORITY || (sending && WINDOW_UPDATE)) {
/* No state change */
} else {
connectionError = 'PROTOCOL_ERROR';
@ -538,7 +538,7 @@ Stream.prototype._transition = function transition(sending, frame) {
case 'HALF_CLOSED_REMOTE':
if (RST_STREAM || (sending && frame.flags.END_STREAM)) {
this._setState('CLOSED');
} else if (BLOCKED || ALTSVC || sending || PRIORITY || (receiving && WINDOW_UPDATE)) {
} else if (ORIGIN || ALTSVC || sending || PRIORITY || (receiving && WINDOW_UPDATE)) {
/* No state change */
} else {
connectionError = 'PROTOCOL_ERROR';
@ -569,7 +569,7 @@ Stream.prototype._transition = function transition(sending, frame) {
if (PRIORITY || (sending && RST_STREAM) ||
(receiving && WINDOW_UPDATE) ||
(receiving && this._closedByUs &&
(this._closedWithRst || RST_STREAM || ALTSVC))) {
(this._closedWithRst || RST_STREAM || ALTSVC || ORIGIN))) {
/* No state change */
} else {
streamError = 'STREAM_CLOSED';