此题来自nodeschool第二个教学模块Stream Adventure,这个题是第七题HTTP SERVER 题目的要求是把通过浏览器POST进来的内容转成大写直接输出给浏览器(最下面贴有完整题目)
我的实现如下
var tr = through(function(buf){
this.queue(buf.toString().toUpperCase());
});
var server = http.createServer(function(req,res){
if(req.method == 'POST'){
req.pipe(tr).pipe(res);
}else{
res.end('please send a post request');
}
});
server.listen(Number(process.argv[2]));
但很可惜通不过测试,测试过程如下
ACTUAL EXPECTED
“YET” “YET”
“HOW” “HOW”
“TO” “TO”
“BURGEON.” “BURGEON.”
“IT’S” “IT’S”
“MEANT” !== “MILLIEMS”
“YET” !== “MEANT”
“HOW” !== “OF”
“TO” !== “MILLIEMS”
“BURGEON.” !== “CENTIMENTS”
“IT’S” !== “OF”
“MILLIEMS” !== “DEADLOST”
“MEANT” !== “CENTIMENTS”
“OF” !== “OR”
“MILLIEMS” !== “DEADLOST”
“CENTIMENTS” !== “MISLAID”
“OF” !== “OR”
“DEADLOST” !== “ON”
“CENTIMENTS” !== “MISLAID”
“OR” !== “THEM”
“DEADLOST” !== “ON”
“MISLAID” !== “BUT,”
我发现开始数个测试都通过了,但是到了后面,发现输出的内容比输入慢,每次对应的输出都是之前输入的内容. 这是我比较疑惑的地方,为什么会出现这种情况.
而正确答案的代码是这样的
var http = require('http');
var through = require('through');
var server = http.createServer(function (req, res) {
if (req.method === 'POST') {
req.pipe(through(function (buf) {
this.queue(buf.toString().toUpperCase());
})).pipe(res);
}
else res.end('send me a POST\n');
});
server.listen(parseInt(process.argv[2]));
不知道这两种处理有什么区别,我只是把内建函数换成了变量.
题目的全部内容如下
<a name=“questrue” id=“questrue”>HTTP SERVER</a>
In this challenge, write an http server that uses a through stream to write back the request stream as upper-cased response data for POST requests.
Streams aren’t just for text files and stdin/stdout. Did you know that http
request and response objects from node core’s http.createServer()
handler are
also streams?
For example, we can stream a file to the response object:
var http = require('http');
var fs = require('fs');
var server = http.createServer(function (req, res) {
fs.createReadStream('file.txt').pipe(res);
});
server.listen(process.argv[2]);
This is great because our server can response immediately without buffering everything in memory first.
We can also stream a request to populate a file with data:
var http = require('http');
var fs = require('fs');
var server = http.createServer(function (req, res) {
if (req.method === 'POST') {
req.pipe(fs.createWriteStream('post.txt'));
}
res.end('beep boop\n');
});
server.listen(process.argv[2]);
You can test this post server with curl:
$ node server.js 8000 &
$ echo hack the planet | curl -d[@-](/user/-) http://localhost:8000
beep boop
$ cat post.txt
hack the planet
Your http server should listen on the port given at process.argv[2] and convert the POST request written to it to upper-case using the same approach as the TRANSFORM example.
As a refresher, here’s an example with the default through callbacks explicitly defined:
var through = require('through')
process.stdin.pipe(through(write, end)).pipe(process.stdout);
function write (buf) { this.queue(buf) }
function end () { this.queue(null)
Do that, but send upper-case data in your http server in response to POST data.
Make sure to npm install through
in the directory where your solution file
lives.
To verify your program, run: stream-adventure verify program.js
.
答案和你的代码的唯一区别是,答案对于每一个http请求创建一个through对象,你是用的全局唯一的through对象。
具体原因还不是很清楚-。-
一语点醒了我,答案确实对于每一次的http请求都创建了一个through对象.这也许是造成错误的原因. 我猜测原因是这样的 : 题目中提到了This is great because our server can response immediately without buffering everything in memory first. 这句话说明 through是没有缓存机制的(没有存入内存),如果多个http并发请求共有一个through对象来处理,可能会发生有内容丢失.
我觉得我发现问题了。
你可以自己重新写一份代码,像这样
var http = require('http');
var through = require('through');
var server = http.createServer(function (req, res) {
if (req.method === 'POST') {
req.pipe(through(function (buf) {
this.queue(buf.toString().toLowerCase());
})).pipe(res);
}
else res.end('send me a POST\n');
});
server.listen(parseInt(process.argv[2]));
这份代码和标准答案的唯一区别是输出小写。
然后你可以发现你还是可以通过测试。
然后,通过阅读stream-adventures的代码,我发现问题。
在nodeschool的verify框架中,verify功能作为一个函数实现,这个函数接受两个函数(a和b)和其他参数作为参数,verify首先调用a,b函数分别创建两个流,a是测试流,b是标准答案流。而在每个问题的目录下面会有一个setup.js,这个js在正式调用verify前被调用,将会创建verify所需要的参数,包括a函数和b函数。
在stream-adventures/problems/http_server/setup.js中,创建的参数是这样的:
return {
a: function (args) { return run(args, aPort) },
b: function (args) { return run(args, bPort) },
close: function () { close.forEach(function (f) { f() }) }
};
可以清晰的看到a函数和b函数的定义
而run函数的实现如下:
function run (args, port) {
var ps = spawn(process.execPath, args.concat(port));
ps.stderr.pipe(process.stderr);
close.push(function () { ps.kill() });
var stream = check(aPort);
if (opts.run) {
ps.stdout.pipe(process.stdout);
stream.on('end', function () { ps.kill() });
}
return stream;
}
其中check
函数,经过我的阅读,实际功能为创建一个输入输出流,向指定端口的服务器发送测试数据,并且将测试结果写入流中待测试。
这其中的bug就很明显了。创建check流的时候,代码为var stream = check(aPort);
这就意味着a函数和b函数创建的测试流是同一个流,标准代码根本就没有被测试。在改为var stream = check(port);
后,工作正常。
然后回过来看看为什么你的代码(实际是正确的)在这种情况下不能正常工作。
实际多次测试后,我们可以发现LZ所列举的错误情况并不是唯一的,也会出现多次错对交替。在这个模型下,两个客户端几乎同时开始运行,然后开始向服务器发送相同的测试数据。由于你使用全局的trough
对象,这个时候a流发送的东西可能流向a流或者b流,b流也是如此,所以说能对能错完全看调度了。。。基本是靠缘分。在使用了标准答案后,由于之前提到的原因,不会造成流混乱,也就可以通过测试。
嗯嗯,好详细的回复啊,非常感谢,我先消化一下您的回复.
@backsapce 主要意思就是,在这道题中,你的代码是正确的,错误的原因在用于测试的代码中
才初学,我得慢慢笑话了,不过setup函数应该有点问题,既然都传了port参数,里面却是用了aport…