Skip to content

Connectの仕組み #60

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Sep 16, 2015
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 66 additions & 2 deletions ja/connect/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Echoサーバとは、送られてきたリクエストの内容をそのまま
```

`app.use(middleware)` という形で、_middleware_と呼ばれる関数には`request`や`response`といったオブジェクトが渡されます。
そのため、リクエストみてフィルタリングしたり、任意のレスポンスを返したり出来るようになっています。
この`request`や`response`を_middleware_で処理することでログを取ったり、任意のレスポンスを返したり出来るようになっています。

Echoサーバでは `req.pipe(res);` という形でリクエストをそのままレスポンスとして流す事で実現されています。

Expand All @@ -47,4 +47,68 @@ Echoサーバでは `req.pipe(res);` という形でリクエストをそのま
基本的にどの_middleware_も`app.use(middleware)`という形で拡張でき、
モジュールとして実装すれば再利用もしやすい形となっています。

> **Note** _middleware_となる関数の引数が4つであると、それはエラーハンドリングの_middleware_とするという、Connectの独自のルールがあります。
> **Note** _middleware_となる関数の引数が4つであると、それはエラーハンドリングの_middleware_とするという、Connect独自のルールがあります。

## どういう仕組み

Connectの_middleware_がどのような仕組みで動いているのかを見ていきます。

`app`に登録した_middleware_は、リクエスト時に呼び出されているので、
`app`のどこかに利用する_middleware_を保持していることは推測できると思います。
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

少し飛躍がある気がする


Connectでは`app.stack`にまだに_middleware_が保持されています。
次のような`app.stack`の中身を表示見ることで、_middleware_が登録順で保持されていることがわかります。

[import connect-trace-example.js](../../src/connect/connect-trace-example.js)

後は、サーバへリクエストがやってきた時に、それぞれの_middleware_を順番に呼び出しています。
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

リクエストされた時に
リクエストが行われた時に 💎

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

でもこれはConnect視点で書いたほうがいいか

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

サーバがリクエストを受け取った時に

が良さそう


上記の例だと以下の順番で_middleware_が呼び出されることになります。

- errorHandler
- nosniff
- hello

エラーハンドリングの_middleware_はエラー時のみ呼ばれるため例外なので、
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なので => となるので

[nosniff.js](#nosniff.js) -> [hello.js](#hello.js) と呼び出されます。

[import nosniff.js](../../src/connect/nosniff.js)

`nosniff.js`は、処理が終わったら`next()`を呼び出していて、
この`next()`が次の_middleware_へ行くという意味になります。

次に、`hello.js`を見てみると、`next()`がないことがわかります。

[import hello.js](../../src/connect/hello.js)

`next()`がないということは`hello.js`がこの連続する_middleware_の最後となっていることがわかります。
仮に、これより先に_middleware_が登録されていたとしても無視されます。

つまり、処理的には以下のようにstackを先頭から一個づつ取り出して、処理していくという方法が取られています。

```js
let req = "...",
res = "...";
function next(){
let middleware = app.stack.shift();
// nextが呼ばれれば次のmiddleware
middleware(req, res, next);
}
next();// 初回
```


このような_middleware_を繋げた形を_middleware stack_と呼ぶことがあります。

HTTPサーバではこのような_middleware stack_を作って使うものは既にあり、
PythonのWSGI MiddlewareやRubyのRackなどが該当します。
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

このような_middleware stack_を作って使うものは => HTTPサーバ => 設計思想として


Connectは`use`というメソッドで_middleware_を使うことからも分かりますが、
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rackを触ったことがある人はお気づきかもしれませんが?
と入れる?

Rackを参考にして実装されています。

- [Ruby - Rack解説 - Rackの構造とRack DSL - Qiita](http://qiita.com/higuma/items/838f4f58bc4a0645950a#2-5 "Ruby - Rack解説 - Rackの構造とRack DSL - Qiita")

次に、この_middleware stack_をどう処理しているのかを、
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

を を となってる

具体的な実装を書きながら見て行きましょう。

## 実装してみよう
2 changes: 2 additions & 0 deletions src/connect/connect-example.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import assert from "assert";
import connect from "connect";
import http from "http";
import fetch from "node-fetch";

const responseText = "response text";
let app = connect();
// add Error handling
Expand All @@ -14,6 +15,7 @@ app.use(errorHandler());
app.use(nosniff());
// respond to all requests
app.use(hello(responseText));

//create node.js http server and listen on port
let server = http.createServer(app).listen(3000, request);

Expand Down
22 changes: 22 additions & 0 deletions src/connect/connect-trace-example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"use strict";
import errorHandler from "./errorHandler";
import hello from "./hello";
import nosniff from "./nosniff";
import connect from "connect";

const responseText = "response text";
let app = connect();
// add Error handling
app.use(errorHandler());
// add "X-Content-Type-Options" to response
app.use(nosniff());
// respond to all requests
app.use(hello(responseText));

// print middleware list
app.stack.map(({handle}) => console.log(handle));
/* =>
[Function: errorHandling]
[Function: nosniff]
[Function: hello]
*/
4 changes: 2 additions & 2 deletions src/connect/errorHandler.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use strict";
export default function errorHandler() {
return function (err, req, res, next) {
export default function () {
return function errorHandling(err, req, res, next) {
console.error(err.stack);
res.status(500).send(err.message);
next();
Expand Down
2 changes: 1 addition & 1 deletion src/connect/hello.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use strict";
export default function (text) {
return function (req, res) {
return function hello(req, res) {
res.end(text);
};
}
2 changes: 1 addition & 1 deletion src/connect/nosniff.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function setHeaders(res, headers) {
});
}
export default function () {
return function (req, res, next) {
return function nosniff(req, res, next) {
setHeaders(res, {
"X-Content-Type-Options": "nosniff"
});
Expand Down