function srcHook(url) {
let nUrl = url.replace("hook-before", "hook-after");
return nUrl;
}
var xhr = new XMLHttpRequest();
xhr.timeout = 3000;
xhr.ontimeout = function (event) {
alert("请求超时!");
}
xhr.open('GET', '/data/hook-before.txt');
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
resolve(xhr.responseText);
WriteLogs("====响应 " + xhr.responseText);
}
}
可以看到,传入URL参数的方法是xhr.open,所以我们重写XMLHttpRequest的open方法进行拦截。重写前,需要先保存一下原生方法。
好了,现在开始正式Hook了:
var $open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function () {
if (srcHook) {
var src = srcHook(arguments[1]);
if (src === false) return;
if (src) {
arguments[1] = src;
}
}
return $open.apply(this, arguments);
}
是的,就是这么简单,在重写的open方法中,对URL参数进行修改,然后调用原生方法。
通过我们的日志信息,可以看到,访问修改已经成功:
没问题,Hook成功。但是,你看下面,还有一个fetch类型的请求没有Hook到,别着急,马上处理它。
仍然首先来看一下fetch的调用方法:
fetch("/data/hook-before.txt")
.then(function(response){
return response.text();
}).then(function(text){
alert(text);
})
fetch是一个全局函数,第一个参数为需要请求的网址。我们只需要重写window对象上的fetch函数即可。
var $fetch = window.fetch;
window.fetch = function () {
if (srcHook) {
var src = srcHook(arguments[0]);
if (src === false) return;
if (src) {
arguments[0] = src;
}
}
return $fetch.apply(window, arguments);
}
这下没问题了,两种请求方式都拦截了。
JSONP来进行跨域请求。
我们仍然是先来看一下JSONP的实现:
var url="/data/hook-before.js";
var script = document.createElement('script');
script.setAttribute('src', url);
document.getElementsByTagName('head')[0].appendChild(script);
可以看出,JSONP的本质是向DOM中插入一个SCRIPT的Element。从代码中,我轻松的找到的Hook点,Element实例的setAttribute方法。
var $setAttribute = Element.prototype.setAttribute;
Element.prototype.setAttribute = function () {
if (this.tagName=="SCRIPT"&&arguments[0]=="src"&&srcHook) {
var src = srcHook(arguments[1]);
if (src === false) return;
if (src) {
arguments[1] = src;
}
}
return $setAttribute.apply(this, arguments);
}
和xhr的hook完全一样。
通过同样的方法,也可以把然而,上面的hook好像也差了点啥。请看下面的代码:img、link、iframe、a给hook掉。
var url="/data/hook-before.js";
var script = document.createElement('script');
script.src=url;
document.getElementsByTagName('head')[0].appendChild(script);
没错,不调用setAttribute方法一样可以设置src。
先看一看src在原型链上的定义:
{get: ƒ, set: ƒ, enumerable: true, configurable: true}
通过定义可以知道src的属性描述符(property descriptor)就可以重写的,这下好办了,我们重写一下src的setter。
var descriptor=Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype, "src");
var setter=descriptor["set"];
descriptor["set"]=function(value){
if (srcHook) {
var src = srcHook(arguments[0]);
if (src === false) return;
if (src) {
arguments[0] = src;
}
}
return setter.apply(this, arguments);
}
descriptor["configurable"]=false;
//由于src的set有可能会被其它脚本修改回去,此处通过设置configurable=false来强行禁止修改
Object.defineProperty(HTMLScriptElement.prototype, "src", descriptor);
通过同样的方法,也可以把提示:img、link、iframe、a,style中和URL相关的属性处理掉。
innerHTML也是通过这种方法进行处理。
background-image属性发起。
document.getElementById("#id").style.background="url(/data/hook-before.jpg)";
CSS属性属于CSSStyleDeclaration对象,该对象的原型上有以下属性可以发起请求:
CSSStyleDeclaration的setProperty方法进行属性设置,所以我们需要在CSSStyleDeclaration的原型链上定义上面的属性,通过设置setter和getter,然后调用setProperty方法进行实际设置。代码示例如下:
Object.defineProperty(CSSStyleDeclaration.prototype, "background",
{
get: function () {
return this.getPropertyValue("background");
},
set: function (v) {
v=srcHook(v);
this.setProperty("background", v);
}
}
);
Object.defineProperty(CSSStyleDeclaration.prototype, "background-image",
{
get: function () {
return this.getPropertyValue("background-image");
},
set: function (v) {
v=srcHook(v);
this.setProperty("background-image", v);
}
}
);
var descriptor = Object.getOwnPropertyDescriptor(CSSStyleDeclaration.prototype, "setProperty");
var valuer = descriptor["value"];
descriptor["value"] = function () {
if (srcHook) {
var src = srcHook(arguments[1]);
if (src === false) return;
if (src) {
arguments[1] = src;
}
}
return valuer.apply(this, arguments);
}
descriptor["configurable"] = false;
//由于src的set有可能会被其它脚本修改回去,此处通过设置configurable=false来强行禁止修改
Object.defineProperty(CSSStyleDeclaration.prototype, "setProperty", descriptor);
由于在对background-image,background等属性进行hook时,调用了setProperty方法进行设置,若原代码中直接就调用的setProperty方法进行设置,则需要对setProperty的属性描述符(property descriptor)进行重写。
MutationObserver监听DOM对象的创建,对于其中的a标签,可以修改href属性。对于img的src属性也可以修改,但无法阻止请求的发出,修改后的请求也会正常发出。
我们先在HTML中添加一个图片显示的DOM
<img src="/data/hook-before.jpg" />
在没有监听和修改前,页面显示的是HOOK前的图片,如下:
然后,我们在JS中添加监听和修改的代码,我们仅用IMG进行测试:
function DomWatch() {
// part 1
var observer = new MutationObserver(function(mutationsList, mutationObserver){
mutationsList.forEach(function(mutation){
if(!mutation.addedNodes) return;
mutation.addedNodes.forEach(function(node){
if(node.tagName!=="IMG") return;
node.src=srcHook(node.src);
})
})
});
// part 2
observer.observe(document, {childList:true,attributes:true,subtree:true});
}
DomWatch();
保存,然后刷新一下页面,可以发现显示的图片已经发生了改变。
在这里,虽然我们看到的图片已经发生了变化,但实际是在HTML中指定的图片依然会发出请求。
在Developer Tools的网络标签中,可以看到,发出了两次图片请求。
关于MutationObserver的具体用法,请可以参考
在HTML中的DOM,也可以通过遍历的方式进行修改,但是如果用innerHTML创建的DOM,处理上就会比较麻烦。
new的时候指定的,如下:
new WebSocket("ws://121.40.165.18:8800")
我们需要拦截WebSocket的new操作,并将连接地址修改为我们需要的地址,对于new的拦截,这里使用ES6的Proxy进行处理。在这里,我们统一将地址修改为ws://119.29.3.36:6700/。
const __WebSocket = new Proxy(window.WebSocket, {
construct(target, args) {
args[0]="ws://119.29.3.36:6700/";
return new target(...args);
}
});
window.WebSocket = __WebSocket;
location或location.href来重定向页面地址,则无法对这个动作进行拦截,location对象已经被浏览器定义为了不可伪造,目前没有找到好的办法,只能通过服务端代理,将调用该属性的js代码进行替换。
通过属性描述可以看到,window上的location和location.href均设置了不可修改。
{enumerable: true, configurable: false, get: ƒ, set: ƒ}
在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这
rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送
我是Ruby的新手。我试过查看在线文档,但没有找到任何有效的方法。我想在以下HTTP请求botget_response()和get()中包含一个用户代理。有人可以指出我正确的方向吗?#PreliminarycheckthatProggitisupcheck=Net::HTTP.get_response(URI.parse(proggit_url))ifcheck.code!="200"puts"ErrorcontactingProggit"returnend#Attempttogetthejsonresponse=Net::HTTP.get(URI.parse(proggit_url)
在我的路线文件中我有:match'graphs/(:id(/:action))'=>'graphs#(:action)'如果是GET请求(工作)或POST请求(不工作),我想匹配它我知道我可以使用以下方法在资源中声明POST请求:post'/'=>:show,:on=>:member但是我怎样才能为比赛做到这一点呢?谢谢。 最佳答案 如果你同时想要POST和GETmatch'graphs/(:id(/:action))'=>'graphs#(:action)',:via=>[:get,:post]编辑默认值可以设置如下match'g
在运行时的方法中,有没有办法知道该方法是否已通过子类中的super调用?例如moduleSuperDetectordefvia_super?#whatgoeshere?endendclassFooincludeSuperDetectordefbarvia_super??'super!':'nothingspecial'endendclassFu"nothingspecial"Fu.new.bar#=>"super!"我如何编写via_super?,或者,如果需要,如何编写via_super?(:bar)? 最佳答案 可能有更好的方法
我试图像这样在我的测试用例中执行获取:request.env['CONTENT_TYPE']='application/json'get:index,:application_name=>"Heka"虽然,它失败了:ActionView::MissingTemplate:Missingtemplatealarm_events/indexwith{:handlers=>[:builder,:haml,:erb,:rjs,:rhtml,:rxml],:locale=>[:en,:en],:formats=>[:html]尽管在我的Controller中我有:respond_to:html,
最好用一个例子来解释:文件1.rb:deffooputs123end文件2.rb:classArequire'file1'endA.new.foo将给出错误“':调用了私有(private)方法'foo'”。我可以通过执行A.new.send("foo")来解决这个问题,但是有没有办法公开导入的方法?编辑:澄清一下,我没有混淆include和require。另外,我不能使用正常包含的原因(正如许多人正确指出的那样)是因为这是元编程设置的一部分。我需要允许用户在运行时添加功能;例如,他可以说“run-this-app--includefile1.rb”,应用程序的行为将根据他在file1
如果使用rspec请求花费的时间太长,我该如何测试行为?我正在考虑使用线程来模拟这个:describe"Test"doit"shouldtimeoutiftherequesttakestoolong"dolambda{thread1=Thread.new{#net::httprequesttogoogle.com}thread2=Thread.new{sleep(xxseconds)}thread1.jointhread2.join}.shouldraise_errorendend我想确保在第一次发出请求后,另一个线程“启动”,在这种情况下只是休眠xx秒。然后我应该期望请求超时,因为执
假设我有:get'/'do$random=Random.rand()response.body=$randomend如果我每秒有数千个请求到达/,$random是否会被共享并“泄漏”到上下文之外,或者它会像getblock的“本地”变量一样?我想如果它是在get'/'do的上下文之外定义的,它确实会被共享,但我想知道在ruby中是否有我不知道的$机制。 最佳答案 ThispartoftheSinatraREADMEaboutscopeisalwayshelpfultoread但是,如果您只需要为请求保留变量,那么我认为我建议使用
运行以下命令时:rvminstall1.9.3我得到以下输出:Error:therequestedURLdoesnotexist:ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-.tar.bz2我已将rvm更新到最新版本并输入rvmreload有什么想法吗? 最佳答案 URL应该是这样的:ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p194.tar.bz2尝试更新您的rvmrvmgethead然后安装1.9.3rvminstall1.9.3