Repository: koajs/koa
Pull request #1145 (2018-02-11 09:37:39)

feat: ignore set/remove header/status when header sent
#1137 

codecov[bot]: # [Codecov](https://codecov.io/gh/koajs/koa/pull/1145?src=pr&el=h1) Report
> Merging [#1145](https://codecov.io/gh/koajs/koa/pull/1145?src=pr&el=desc) into [v1.x](https://codecov.io/gh/koajs/koa/commit/494a365427bfacef67caae9593ed196b44bd491e?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/koajs/koa/pull/1145/graphs/tree.svg?token=cvMfaQw3td&src=pr&width=650&height=150)](https://codecov.io/gh/koajs/koa/pull/1145?src=pr&el=tree)

```diff
@@         Coverage Diff          @@
##           v1.x   #1145   +/-   ##
====================================
  Coverage   100%    100%           
====================================
  Files         4       4           
  Lines       428     432    +4     
  Branches    105     109    +4     
====================================
+ Hits        428     432    +4
```


| [Impacted Files](https://codecov.io/gh/koajs/koa/pull/1145?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [lib/response.js](https://codecov.io/gh/koajs/koa/pull/1145/diff?src=pr&el=tree#diff-bGliL3Jlc3BvbnNlLmpz) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/koajs/koa/pull/1145?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/koajs/koa/pull/1145?src=pr&el=footer). Last update [494a365...29a2b89](https://codecov.io/gh/koajs/koa/pull/1145?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/lib/response.js b/lib/response.js
index 21a8b7f..a51d661 100644
--- a/lib/response.js
+++ b/lib/response.js
@@ -79,6 +79,8 @@ module.exports = {
    */
 
   set status(code) {
+    if (this.headerSent) return;
+
     assert('number' == typeof code, 'status code must be a number');
     assert(statuses[code], 'invalid status code: ' + code);
     this._explicitStatus = true;
@@ -229,6 +231,8 @@ module.exports = {
    */
 
   vary: function(field){
+    if (this.headerSent) return;
+
     vary(this.res, field);
   },
 
@@ -430,6 +434,8 @@ module.exports = {
    */
 
   set: function(field, val){
+    if (this.headerSent) return;
+
     if (2 == arguments.length) {
       if (Array.isArray(val)) val = val.map(String);
       else val = String(val);
@@ -475,6 +481,8 @@ module.exports = {
    */
 
   remove: function(field){
+    if (this.headerSent) return;
+
     this.res.removeHeader(field);
   },
 


Test patch:
diff --git a/test/response/remove.js b/test/response/remove.js
index b74c46d..723ffdf 100644
--- a/test/response/remove.js
+++ b/test/response/remove.js
@@ -2,6 +2,7 @@
 'use strict';
 
 var context = require('../context');
+var should = require('should');
 
 describe('ctx.remove(name)', function(){
   it('should remove a field', function(){
@@ -10,4 +11,14 @@ describe('ctx.remove(name)', function(){
     ctx.remove('x-foo');
     ctx.response.header.should.eql({});
   })
+
+  describe('after header sent', function(){
+    it('should ignore', function(){
+      var ctx = context();
+      ctx.set('foo', 'bar');
+      ctx.res.headersSent = true;
+      ctx.remove('foo', 'bar');
+      ctx.response.header.foo.should.equal('bar');
+    })
+  })
 })
diff --git a/test/response/set.js b/test/response/set.js
index 45a304a..a12ab8f 100644
--- a/test/response/set.js
+++ b/test/response/set.js
@@ -2,6 +2,7 @@
 'use strict';
 
 var context = require('../context');
+var should = require('should');
 
 describe('ctx.set(name, val)', function(){
   it('should set a field value', function(){
@@ -21,6 +22,14 @@ describe('ctx.set(name, val)', function(){
     ctx.set('x-foo', ['foo', 'bar']);
     ctx.response.header['x-foo'].should.eql([ 'foo', 'bar' ]);
   })
+
+  describe('after header sent', function(){
+    it('should ignore', function(){
+      var ctx = context(null, { headersSent: true });
+      ctx.set('foo', 'bar');
+      should.not.exist(ctx.response.header.foo);
+    })
+  })
 })
 
 describe('ctx.set(object)', function(){
diff --git a/test/response/status.js b/test/response/status.js
index 744a6d9..50abb58 100644
--- a/test/response/status.js
+++ b/test/response/status.js
@@ -5,6 +5,7 @@ var response = require('../context').response;
 var request = require('supertest');
 var statuses = require('statuses');
 var assert = require('assert');
+var should = require('should');
 var koa = require('../..');
 
 describe('res.status=', function(){
@@ -50,6 +51,14 @@ describe('res.status=', function(){
     })
   })
 
+  describe('after header sent', function(){
+    it('should ignore', function(){
+      var res = response(null, { headersSent: true });
+      res.status = 200;
+      should.not.exist(res.status);
+    })
+  })
+
   describe('when a status string', function(){
     it('should throw', function(){
       assert.throws(function() {
diff --git a/test/response/vary.js b/test/response/vary.js
index 2dcb314..9520f49 100644
--- a/test/response/vary.js
+++ b/test/response/vary.js
@@ -2,6 +2,7 @@
 'use strict';
 
 var context = require('../context');
+var should = require('should');
 
 describe('ctx.vary(field)', function(){
   describe('when Vary is not set', function(){
@@ -31,4 +32,12 @@ describe('ctx.vary(field)', function(){
       ctx.response.header.vary.should.equal('Accept, Accept-Encoding');
     })
   })
+
+  describe('after header sent', function(){
+    it('should ignore', function(){
+      var ctx = context(null, { headersSent: true });
+      ctx.vary('Accept');
+      should.not.exist(ctx.response.header.vary);
+    })
+  })
 })