服务端接收并拍卖请求,首先从官网下载FiddlerCoreAPI

1.前言

前一段时间想利用fiddlercore截取本地HTTPS的流量做一些剖析,依据样例代码的诠释学习了弹指间,没搞理解怎么落实,后来又在网上查了些资料,对HTTPS的处理提及很少,都未曾化解自个儿的题材,首借使HTTPS证书的题材,索性自个儿研商了须臾间,终于消除了难题。笔者会在下篇文章中分享下本身的思路,本篇小说先简单解析下fiddlercore自带样例的代码,帮助刚接触fiddlercore的人急速入门,假设有说的非常的地点,欢迎批评指正。

1.前言

前一段时间想行使fiddlercore截取本地HTTPS的流量做一些解析,依据样例代码的诠释学习了弹指间,没搞领会怎么落到实处,后来又在网上查了些资料,对HTTPS的拍卖提及很少,都不曾缓解自个儿的题材,首假设HTTPS证书的标题,索性本身研讨了须臾间,终于化解了问题。小编会在下篇小说中享受下自个儿的思路,本篇作品先不难解析下fiddlercore自带样例的代码,协理刚接触fiddlercore的人快捷入门,要是有说的畸形的地点,欢迎批评指正。

使用node进行web开发

用户上网流程: 表面上看:打开浏览器——输入网址——跳转——上网。
背后的经过是哪些吗?
http请求网址到钦命的主机——服务器收到请求——服务器响应内容到用户浏览器——浏览器接收到数码,并依照接收到的音讯实行拍卖,渲染出用户观望的界面。

名词解释: 客户端——用户浏览器; 服务端——服务端

今昔再简单下描述:
由客户端发送1个http请求到服务端,服务端接收并拍卖请求,再次来到数据到客户端


2.源码分析

率先从官网下载FiddlerCoreAPI
https://www.telerik.com/purchase/fiddlercore

下载下来是一个安装文件,解压后有demo和FiddlerCoreAPI库,打开样例代码工程,发轫分析。

从主函数开始。

List<Fiddler.Session> oAllSessions = new List<Fiddler.Session>();

概念了二个fiddler的Session类的List,里面存放的是客户端和服务端的信息。

Fiddler.FiddlerApplication.SetAppDisplayName("FiddlerCoreDemoApp");

命名本身的应用程序。

Fiddler.FiddlerApplication.OnNotification += delegate(object sender, NotificationEventArgs oNEA) { Console.WriteLine("** NotifyUser: " + oNEA.NotifyString); };
Fiddler.FiddlerApplication.Log.OnLogString += delegate(object sender, LogEventArgs oLEA) { Console.WriteLine("** LogString: " + oLEA.LogString); };

那两句,第③句绑定了用户通报事件的函数,具体怎样时候触发没仔细切磋,第贰句绑定了FiddlerApplication.Log触发的轩然大波(FiddlerCore自身的日记系统),后边打字与印刷内容都会用到,显而易见这两句正是把内容打字与印刷在控制台上。

Fiddler.FiddlerApplication.BeforeRequest += delegate(Fiddler.Session oS)
{
    // Console.WriteLine("Before request for:\t" + oS.fullUrl);
    oS.bBufferResponse = false;
    Monitor.Enter(oAllSessions);
    oAllSessions.Add(oS);
    Monitor.Exit(oAllSessions);

    if ((oS.oRequest.pipeClient.LocalPort == iSecureEndpointPort) && (oS.hostname == sSecureEndpointHostname))
    {
        oS.utilCreateResponseAndBypassServer();
        oS.oResponse.headers.SetStatus(200, "Ok");
        oS.oResponse["Content-Type"] = "text/html; charset=UTF-8";
        oS.oResponse["Cache-Control"] = "private, max-age=0";
        oS.utilSetResponseBody("<html><body>Request for httpS://" + sSecureEndpointHostname + ":" + iSecureEndpointPort.ToString() + " received. Your request was:<br /><plaintext>" + oS.oRequest.headers.ToString());
    }
};

BeforeRequest,顾名思义,正是在客户端发送请求后拦截之,在此函数中得以获取甚至修改请求的始末。

oS.fullUrl为呼吁的UEnclaveL。

bBufferResponse这几个脾气,要安装成true才能够修改服务器响应的内容。

接下去的if判断,是五个例子,借使您拜访https://localhost:7777,他会拦截你的请求,并构造响应报文返回给你,服务端不会收到该请求。

Fiddler.FiddlerApplication.Startup(iPort, oFCSF);

拉开FiddlerCore在钦赐端口的监听。

oSecureEndpoint = FiddlerApplication.CreateProxyEndpoint(iSecureEndpointPort, true, sSecureEndpointHostname);

树立在钦定端口的HTTPS的监听,那一个函数下篇文章中还会说到。

从这之后,那一个样例的第③代码就分析完了,它达成了简短的http请求截获和响应替换,剩余的代码都以些与用户的竞相,这里不再赘言。

2.源码分析

先是从官网下载FiddlerCoreAPI
https://www.telerik.com/purchase/fiddlercore

下载下来是贰个安装文件,解压后有demo和FiddlerCoreAPI库,打开样例代码工程,初叶分析。

从主函数伊始。

List<Fiddler.Session> oAllSessions = new List<Fiddler.Session>();

概念了三个fiddler的Session类的List,里面存放的是客户端和服务端的新闻。

Fiddler.FiddlerApplication.SetAppDisplayName("FiddlerCoreDemoApp");

命名自个儿的应用程序。

Fiddler.FiddlerApplication.OnNotification += delegate(object sender, NotificationEventArgs oNEA) { Console.WriteLine("** NotifyUser: " + oNEA.NotifyString); };
Fiddler.FiddlerApplication.Log.OnLogString += delegate(object sender, LogEventArgs oLEA) { Console.WriteLine("** LogString: " + oLEA.LogString); };

那两句,第叁句绑定了用户通报事件的函数,具体怎么时候触发没仔细斟酌,第①句绑定了FiddlerApplication.Log触发的轩然大波(FiddlerCore自身的日志系统),前边打字与印刷内容都会用到,总而言之那两句就是把内容打字与印刷在控制台上。

Fiddler.FiddlerApplication.BeforeRequest += delegate(Fiddler.Session oS)
{
    // Console.WriteLine("Before request for:\t" + oS.fullUrl);
    oS.bBufferResponse = false;
    Monitor.Enter(oAllSessions);
    oAllSessions.Add(oS);
    Monitor.Exit(oAllSessions);

    if ((oS.oRequest.pipeClient.LocalPort == iSecureEndpointPort) && (oS.hostname == sSecureEndpointHostname))
    {
        oS.utilCreateResponseAndBypassServer();
        oS.oResponse.headers.SetStatus(200, "Ok");
        oS.oResponse["Content-Type"] = "text/html; charset=UTF-8";
        oS.oResponse["Cache-Control"] = "private, max-age=0";
        oS.utilSetResponseBody("<html><body>Request for httpS://" + sSecureEndpointHostname + ":" + iSecureEndpointPort.ToString() + " received. Your request was:<br /><plaintext>" + oS.oRequest.headers.ToString());
    }
};

BeforeRequest,顾名思义,就是在客户端发送请求后拦截之,在此函数中能够收获甚至修改请求的内容。

oS.fullUrl为呼吁的UWranglerL。

bBufferResponse这脾本性,要安装成true才能够修改服务器响应的始末。

接下去的if判断,是一个事例,即使您拜访https://localhost:7777,他会拦截你的请求,并构造响应报文返回给你,服务端不会收到该请求。

Fiddler.FiddlerApplication.Startup(iPort, oFCSF);

打开FiddlerCore在钦命端口的监听。

oSecureEndpoint = FiddlerApplication.CreateProxyEndpoint(iSecureEndpointPort, true, sSecureEndpointHostname);

创建在钦点端口的HTTPS的监听,那一个函数下篇小说中还会说到。

从这之后,这么些样例的重中之重代码就分析完了,它完毕了大约的http请求截获和响应替换,剩余的代码都以些与用户的交互,这里不再赘言。

搭服务器

收发数据遵从一定的条条框框————一般是http规则。
搭建服务器,处理http请求,须要node.js提供2个http模块。

//加载一个http模块
var http=require('http');
//通过http模块下的creatServer方法创建并返回一个服务器对象
var server=http.createServer();
//开启服务器,接收四个参数
// 参数第一个是端口,第二个是ip,第三个是链接等待队列的最大长度,第四个成功开启之后的回调
server.listen();

本条时候输入console.log(server.address()),结果回到:
{ address: '::', family: 'IPv6', port: 57379 }
port端口号每一趟都转移,所以一般要团结安装。端口号尽量设大点。

3.注意

眼下代码执行完后,一定要调用Shutdown()函数关闭FiddlerCore应用,否则会导致浏览器依然经过Fiddler代理,上连发网。

Fiddler.FiddlerApplication.Shutdown();

在申明管理器中得以看出FiddlerCore安装了本人的注明。

图片 1

阅读样例代码的诠释很有援助,还有FiddlerCore的赞助文书档案,里面各样函数的意义说的很详细。

3.注意

近年来代码执行完后,一定要调用Shutdown()函数关闭FiddlerCore应用,不然会招致浏览器依旧通过Fiddler代理,上穿梭网。

Fiddler.FiddlerApplication.Shutdown();

在证明管理器中能够看来FiddlerCore安装了上下一心的证书。

图片 2

阅读样例代码的注释很有扶助,还有FiddlerCore的赞助文书档案,里面各样函数的功用说的很详细。

http – http模块 – require(‘http’)

var http = require(‘http’);

var server = http.createServer([requestListener])

–成立并回到1个HTTP服务器对象 –requestListener :
监听到客户端连接的回调函数

server.listen(port, [hostname], [backlog], [callback])

–监听客户端连接请求,唯有当调用了listen方法之后,服务器才起来工作 –port
: 监听的端口 –hostname : 主机名(IP/域名) –backlog :
连接等待队列的最大尺寸 –callback :
调用listen方法并成功打开监听以往,会触发2个listening事件,callback将作为该事件的进行函数

listening事件:当server调用listen方法并成功始于监听现在触发的事件

error事件 : 当服务开启退步的时候接触的风云

–参数err : 具体的荒谬对象

任何代码如下,三个服务器就搭建完结了。

//加载一个http模块
var http=require('http');
//通过http模块下的creatServer方法创建并返回一个服务器对象
var server=http.createServer();
//开启服务器,接收四个参数
// 参数第一个是端口,第二个是ip,第三个是链接等待队列的最大长度,第四个成功开启之后的回调

server.on('error',function (err) {//报错方法
  console.log('错误信息:'+err);

});

server.on('listening',function () {
    console.log('服务器正在运行...');
  console.log(server.address());
});//运行提示

server.listen(8088,'localhost');//绑定8088端口

输出结果:

服务器正在运行...
{ address: '127.0.0.1', family: 'IPv4', port: 8088 }

request事件

在地方的代码插入一段

server.on('request',function () {
    console.log('有客户端请求了!')
});

下一场经过浏览器输入127.0.0.1:8088,那时弹出会弹出相应的音讯。
注意:在此以前涉嫌的createServer方法。它事实上接收叁个回调函数,也正是说二者其实是同等的。

request事件 : 当有客户端发送请求到该主机和端口的央求的时候接触

无非接收请求是尚未多疏忽义的。还须求对客户端的央浼进行解析。

  • 参数request :
    http.IncomingMessage的2个实例,通过他大家得以博获得本次请求的片段新闻,比如头音信,数据等。

server.on('request',function (req,res) {
    console.log('有客户端请求了!')
    console.log(req);
});

再一次载入页面,发现第1个参数request是一大串的音信。
里面有成都百货上千常用的措施。

  • httpVersion : 使用的http协议的本子,日常是1.1

  • headers : 请求头新闻中的数据
    图片 3

  • url : 请求的地方

  • method : 请求格局(GET/POST)

  • 参数response :
    http.ServerResponse的叁个实例,通过她我们可以向该次请求的客户端输出再次来到响应
    比如说请求发过来了,不过客户端直接在伺机,而服务器未向客户端发送任何数据。能够由此server.timout方法设置最长等待时间。

  • write(chunk, [encoding]) :
    发送多个数额块到响应正文中,数据块,编码

  • end([chunk], [encoding]) :
    当全体的正文和头信息发送达成未来调用该措施告诉服务器数据现已全体出殡和埋葬完结了,这些主意在历次完结音讯发送今后必须调用,并且是最终调用.

server.on('request',function (req,res) {
   console.log('有客户端请求了!');
 res.write('朕知道你发请求了','utf-8');//光是写入还不行,必须告诉客户端写完了
 res.end();
 //console.log(res);
});

客户端再刷新的结果,厉害了:
图片 4
你也足以在您的文书档案里写如一段html代码。仍是能够安装头新闻。

  • statusCode : 该属性用来安装再次来到的状态码
  • setHeader(name, value) : 设置再次来到头音信
  • writeHead(statusCode, [reasonPhrase],
    [headers])//状态码(200,404等),
    状态码能够经过输入http.STATUS_CODES点击实行查看
    那么些艺术只能在此时此刻呼吁中选择一遍,并且必须在response.end()在此之前调用
    res.writeHead(200,'SUNNY');//定义成功时的描述文字
    图片 5

呼吁参数req里的url处理

上边的服务器有第二的弱点。url无论输入localhost/1….前边长度无论怎么样,重临的都以叁个新闻。
以下大家用case来效仿首页(’/’),个人大旨(’/user’)和404(’defult’)的响应情状新建二个服务器。

var http=require('http');
var server=http.createServer();

server.on('request',function (req,res) {
    //通过req的url属性回应数据
  //?后面的部分是query string查询字符串
  console.log(req.url)
});
server.listen(8089,'localhost');

当访问localhost:8089/xxx时,打字与印刷出来的url为/xxx
再分析下url对象这么些事物。路径名有时候是那多少个1错综复杂的,包含?等等参数。对url进行预处理很有须要,那就提到到url对象

var http=require('http');
//请求url模块,并把url序列化想要的格式
var url=require('url');

var server=http.createServer();

server.on('request',function (req,res) {
    //通过req的url属性回应数据
  //?后面的部分是query string查询字符串
  //console.log(req.url)
  var urlStr=url.parse('http://www.baidu.com/a/index.html?c=2');
  console.log(urlStr);
});

server.listen(8089,'localhost');

获得的打字与印刷出来的是 图片 6
分析上边包车型客车始末发现,我们要求urlStr.pathname

var http=require('http');
//请求url模块,并把url序列化想要的格式
var url=require('url');

var server=http.createServer();

server.on('request',function (req,res) {
    //通过req的url属性回应数据
  //?后面的部分是query string查询字符串
  //console.log(req.url)
  var urlStr=url.parse(req.url);
  //console.log(urlStr);
  switch(urlStr.pathname){
        case '/':
  //首页,没有什么后缀
  res.writeHead(200,{
                'content-type':'text/html;charset=utf-8'
  });
  res.end('<h1>首页</h1>');
  break;
  case '/user':
  //用户首页
  res.writeHead(200,{
                'content-type':'text/html;charset=utf-8'
  });
  res.end('<h1>个人中心</h1>');
  break;
  default:
  res.writeHead(404,{
                'content-type':'text/html;charset=utf-8'
  });
  res.end('<h1>404</h1>');
  //处理其他情况
  break;
  }
});

server.listen(8089,'localhost');

那么url处理就算成功了。

突显和表现的诀别

上边的代码中,每一遍针对每一个页面输入不均等的始末,看起来是十一分麻烦的。
作为规则,把html写进代码也是不符合规范的。也不便利尊敬。
所以大家须求用res的艺术把它读取出来,再停放页面上去。
能够在同目录下新建3个html文件夹,创造首页(index.html)、用户基本(user.html)、404.html并写上个其余始末。然后用fs对象来读它。

var http=require('http');
var url=require('url');
var fs=require('fs');

var server=http.createServer();
var htmlDir='./html/';

server.on('request',function (req,res) {
    var urlStr=url.parse(req.url);
  //console.log(urlStr);
  switch (urlStr.pathname){
        case '/':
  sendData(htmlDir+'index.html',req,res);
  break;
  case '/user':
  sendData(htmlDir+'user.html',req,res);
  break;
  default:
  sendData(htmlDir+'404.html',req,res);
  break;
  }
});

//应该定义一个方法,当根据路径来读取页面,然后把所需要的页面返回出去。
//三个参数:file是文件路径
function sendData(file,req,res) {
    //这个方法的核心是读取文件内容。
  fs.readFile(file,function (err,data) {
        if(err){//如果读取失败,也就是404
  console.log(err);
  res.writeHead(404,{
                'content-type':'text/html;charset=utf-8'
  });
  res.end(data);
  }else{
            res.writeHead(200,{
                'content-type':'text/html;charset=utf-8'
  });
  //console.log(data.toString());
  res.end(data);
  }
    });

}
server.listen('8089','localhost');

功能就完毕了。但实质上代码还留存重重不供给的case语句。尝试再修改:

var http=require('http');
var url=require('url');
var fs=require('fs');

var server=http.createServer();
var htmlDir='./html';

server.on('request',function (req,res) {
    var urlStr=url.parse(req.url);
  switch (urlStr.pathname){
    case '/':
  sendData(htmlDir+'/index.html',req,res);
  break;
  default:
 var urlPath=htmlDir+urlStr.pathname+'.html';
  sendData(urlPath,req,res);
}

});

//应该定义一个方法,当根据路径来读取页面,然后把所需要的页面返回出去。
//三个参数:file是文件路径,
function sendData(file,req,res) {
    //这个方法的核心是读取文件内容。
  fs.readFile(file,function (err,data) {
        if(err){//如果读取失败,也就是404
  console.log(file)
            console.log(err);
  fs.readFile('./html/404.html',function(err,data){
                res.writeHead(404,{
                    'content-type':'text/html;charset=utf-8'
  });
  res.end(data);
  });
  }else{
            res.writeHead(200,{
                'content-type':'text/html;charset=utf-8'
  });
  //console.log(data.toString());
  res.end(data);
  }
    });

}
server.listen('8089','localhost');

在那些代码下,尝试读取任何页面都有回应。笔者在html文件夹下成立了一个新的xxx.html读取也能科学。代码也简化了。这样基本成效就满足了。
遗憾的是时下还不援救css,js外链。

请求处理

为了证实原理,如故使用地点的事例代码。

  • get请求的多少处理

在html文件夹下成立1个login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
<h1>这是登陆页面</h1>
<form action="/login/validate" method="get">
    请输入用户名:<input type="text" name="username"><br>
    请输入密码:<input type="text" name="password"><br>
    <button type="submit">提交!</button>
</form>
</body>
</html>

怎样获取用户提交过来的始末吧?在事先宣称过的urlStr里面就包蕴了这几个数据。

var http=require('http');
var url=require('url');
var fs=require('fs');

var server=http.createServer();
var htmlDir='./html';

server.on('request',function (req,res) {
    var urlStr=url.parse(req.url);

  switch (urlStr.pathname){
        case '/':
  sendData(htmlDir+'/index.html',req,res);
  break;
  case '/login/validate':
  //想得到用户提交过来的数据
  //console.log(req.method)//提交方式get/post
  console.log(urlStr);
  break;
  default:
 var urlPath=htmlDir+urlStr.pathname+'.html';
  sendData(urlPath,req,res);
  }

});

function sendData(file,req,res) {
    //这个方法的核心是读取文件内容。
  fs.readFile(file,function (err,data) {
        if(err){//如果读取失败,也就是404
  console.log(file)
            console.log(err);
  fs.readFile('./html/404.html',function(err,data){
                res.writeHead(404,{
                    'content-type':'text/html;charset=utf-8'
  });
  res.end(data);
  });
  }else{
            res.writeHead(200,{
                'content-type':'text/html;charset=utf-8'
  });
  //console.log(data.toString());
  res.end(data);
  }
    });

}
server.listen('8089','localhost');

登陆locoalhost:8089/login输入用户名密码,提交,发现打字与印刷出下列内容:
图片 7 个中最首要质量是query。
原来用户名是213,密码是231.
怎么落到实处自动化处理啊?node.js提供了必备的方法。

querystring模块 parse() : 将二个 query string 反类别化为二个指标

case '/login/validate':
  //想得到用户提交过来的数据
  //console.log(req.method)//提交方式get/post
  var querystring=require('querystring');
  console.log(querystring.parse(urlStr.query));
  break;

结果打字与印刷出来三个json:{ username: ‘213’, password: ‘231’ }
实在是太有利了。有了它,就足以很有益的处理提交音信。

  • post请求的数目处理:req的data和end事件
    接下来把login.html页面包车型客车提交格局改为post
    post发送的数额会被写入缓冲区中,供给通过resquest的data事件end事件来进展数量拼接处理

if(req.method.toUpperCase()=='POST'){
    var str='';
  req.on('data',function (chunk) {
       str+=chunk;
  });
  req.on('end',function () {
        console.log(str);
  })
}

打字与印刷出来的str为username=123&password=321
对此如此格式写成的数据自然想到了用querystring.parse()进行拍卖。处理完之后就是3个json对象了。

全副代码:

var http=require('http');
var url=require('url');
var fs=require('fs');

var server=http.createServer();
var htmlDir='./html';

server.on('request',function (req,res) {
    var urlStr=url.parse(req.url);

  switch (urlStr.pathname){
        case '/':
  sendData(htmlDir+'/index.html',req,res);
  break;
  case '/login/validate':
  //想得到用户提交过来的数据
  //console.log(req.method)//提交方式get/post
  if(req.method.toUpperCase()=='POST'){
                var str='';
  req.on('data',function (chunk) {
                   str+=chunk;
  });
  req.on('end',function () {
                    console.log(str);
  var querystring=require('querystring');
  console.log(querystring.parse(str));
  });

  }else if(req.method.toUpperCase()=='GET'){
                var querystring=require('querystring');
  console.log(querystring.parse(urlStr.query));
  }
            break;
  default:
 var urlPath=htmlDir+urlStr.pathname+'.html';
  sendData(urlPath,req,res);
  }

});

function sendData(file,req,res) {
    //这个方法的核心是读取文件内容。
  fs.readFile(file,function (err,data) {
        if(err){//如果读取失败,也就是404
  console.log(file)
            console.log(err);
  fs.readFile('./html/404.html',function(err,data){
                res.writeHead(404,{
                    'content-type':'text/html;charset=utf-8'
  });
  res.end(data);
  });
  }else{
            res.writeHead(200,{
                'content-type':'text/html;charset=utf-8'
  });
  //console.log(data.toString());
  res.end(data);
  }
    });

}
server.listen('8089','localhost');

相关文章