如何监听普通请求.
测试引用:
http://www.cnblogs.com/ference/p/4014210.html
2017/09/30 15:03:51 request url: <http://localhost:10011/login>
2017/09/30 15:03:51 request url path: </login>
2017/09/30 15:03:51 request for <localhost:10011> from <127.0.0.1:47038>
2017/09/30 15:03:51 request body is <username=cartman&password=poder>
2017/09/30 15:03:51
2017/09/30 15:03:51 response: localhost:10011 -> text/plain
2017/09/30 15:03:51 response status -> 200 OK
2017/09/30 15:03:51 response status code -> 200
2017/09/30 15:03:51 response body -> logon
2017/09/30 15:03:51 response content-length -> 5
2017/09/30 15:03:51
对Resquest操作
proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
log.Printf("request url: <%v>", req.URL)
log.Printf("request url path: <%v>", req.URL.Path)
log.Printf("request for <%v> from <%v>", req.URL.Host, req.RemoteAddr)
// Attempt 1: // It will drain the Body. Fails.
// bytes, _ := io.ioutil.ReadAll(req.Body)
// Attempt 2: // Fails. GetBody is nil
// body, _ := req.GetBody() // call to nil func
// bytes, _ := ioutil.ReadAll(body) // instead of req.Body
// Attempt 3:
if req.Body != nil {
body, _ := ioutil.ReadAll(req.Body)
req.Body = &FakeReadCloser{bytes.NewReader(body)}
//log.Printf("request body is <%v>", string(body))
}
log.Println()
return req, nil
})
从req中取出body. 不过这个对象读完了之后需要还原. 所以用了一个Faker类. (主要实现ReadCloser)
type ReadCloser struct {
Reader
Closer
}
实现很简单:
type FakeReadCloser struct {
io.Reader
}
func (FakeReadCloser) Close() error {
return nil
}
使用的时候再套上一个bytes.Reader.(至于FakeReadCloser是否是指针, 都可以. 暂不纠结)
对Response操作
proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
if ctx != nil && ctx.Req != nil && resp != nil && resp.Header != nil {
log.Println("response:", ctx.Req.Host, "->", resp.Header.Get("Content-Type"))
}
if resp.Body != nil {
body, _ := ioutil.ReadAll(resp.Body)
log.Println("response status ->", resp.Status) //string
log.Println("response status code ->", resp.StatusCode) // int
//log.Println("response body ->", string(body))
log.Println("response content-length ->", resp.ContentLength)
resp.Body = &FakeReadCloser{bytes.NewReader(body)}
log.Println()
}
return resp
})
不需要其他設置. 即可完成監聽.
測試用js
'use strict';
const http = require('http');
const querystring = require('querystring');
const ProxyHost = "localhost";
const ProxyPort = 8080;
const TargetHost = "localhost";
const TargetPort = 10011;
//~ por ejemplo:
//~ name=eric&passwd=secret
function getResDo(cb) {
return (res) => {
res.setEncoding('utf8');
let rs = '';
res.on('data', (chunk) => {
rs += `${chunk}`;
});
res.on('end', () => {
//console.log("response is %s", resJson);
//console.log(res.headers);
//console.log("====");
//console.log(res.headers.cookie);// undefined
//console.log(res.headers['set-cookie']); // an array
//console.log("Is an array: %s", Array.isArray(res.headers['set-cookie']));
cb(res, rs);
});
};
}
function parseCookies(res) {
let list = {};
let ra = res.headers['set-cookie'];
//~ not every response get a `set-cookie`
if (ra) {
ra.forEach((rc) => {
rc && rc.split(';').forEach((cookie) => {
let parts = cookie.split('=');
list[parts.shift().trim()] = parts.join('=');
});
});
}
return list;
}
function packCookie(opts) {
let ar = [];
for (let k in opts) {
ar.push(`${k}=${opts[k]}`);
}
return ar.join("; ");
}
function makeLoginOptions(dataToPost) {
return {
hostname: ProxyHost,
port: ProxyPort,
path: `http://${TargetHost}:${TargetPort}/login`,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': dataToPost.length,
}
};
}
function doLogin() {
return new Promise((resolve, reject) => {
let postdata = querystring.stringify({
username: 'cartman',
password: 'poder'
});
let req = http.request(makeLoginOptions(postdata), getResDo((res, rs) => {
resolve({
cookie:parseCookies(res),
response:rs
});
}));
req.end(postdata);
});
}
doLogin().then((rs) => {
let cookie = rs.cookie;
let response = rs.response;
console.log("before moving to next:\n%s", JSON.stringify(cookie, null, 4));
console.log("response: %s", response);
});
在這種簡單模式下163和csdn都可以代.
git也能穿.(之前git不能穿的原因是OnResponse中訪問resp的異常)
So far.