Skip to content
Permalink
Browse files

websocket,feat: support add customized http headers (#474)

  • Loading branch information...
asionius authored and xicilion committed Nov 20, 2018
1 parent 7a9ca2b commit 70ff4198a6fa2553b29a6eb9030d50add43a11c6
Showing with 174 additions and 11 deletions.
  1. +8 −0 fibjs/include/ifs/WebSocket.h
  2. +10 −0 fibjs/src/http/http.cpp
  3. +38 −7 fibjs/src/websocket/WebSocket.cpp
  4. +11 −1 idl/zh-cn/WebSocket.idl
  5. +107 −3 test/ws_test.js
@@ -26,6 +26,7 @@ class WebSocket_base : public EventEmitter_base {
public:
// WebSocket_base
static result_t _new(exlib::string url, exlib::string protocol, exlib::string origin, obj_ptr<WebSocket_base>& retVal, v8::Local<v8::Object> This = v8::Local<v8::Object>());
static result_t _new(exlib::string url, v8::Local<v8::Object> opts, obj_ptr<WebSocket_base>& retVal, v8::Local<v8::Object> This = v8::Local<v8::Object>());
virtual result_t get_url(exlib::string& retVal) = 0;
virtual result_t get_protocol(exlib::string& retVal) = 0;
virtual result_t get_origin(exlib::string& retVal) = 0;
@@ -124,6 +125,13 @@ void WebSocket_base::__new(const T& args)

hr = _new(v0, v1, v2, vr, args.This());

METHOD_OVER(2, 1);

ARG(exlib::string, 0);
OPT_ARG(v8::Local<v8::Object>, 1, v8::Object::New(isolate));

hr = _new(v0, v1, vr, args.This());

CONSTRUCT_RETURN();
}

@@ -39,6 +39,16 @@ result_t http_request(exlib::string method, exlib::string url,
return get_httpClient(ac->isolate())->request(method, url, body, headers, retVal, ac);
}

result_t http_request2(HttpClient_base* httpClient, exlib::string method, exlib::string url,
SeekableStream_base* body, NObject* headers,
obj_ptr<HttpResponse_base>& retVal, AsyncEvent* ac)
{
if (httpClient != NULL)
return ((HttpClient*)httpClient)->request(method, url, body, headers, retVal, ac);
else
return get_httpClient(ac->isolate())->request(method, url, body, headers, retVal, ac);
}

result_t http_base::get_cookies(obj_ptr<NArray>& retVal)
{
return get_httpClient()->get_cookies(retVal);
@@ -15,13 +15,14 @@
#include <mbedtls/mbedtls/sha1.h>
#include "encoding.h"
#include "MemoryStream.h"
#include "HttpClient.h"
#include <stdlib.h>

namespace fibjs {

DECLARE_MODULE(ws);

result_t http_request(exlib::string method, exlib::string url,
result_t http_request2(HttpClient_base* httpClient, exlib::string method, exlib::string url,
SeekableStream_base* body, NObject* headers,
obj_ptr<HttpResponse_base>& retVal, AsyncEvent* ac);

@@ -171,15 +172,31 @@ class asyncSend : public AsyncState {
int64_t m_size;
};

result_t WebSocket_base::_new(exlib::string url, exlib::string protocol, exlib::string origin,
result_t WebSocket_base::_new(exlib::string url, exlib::string protocol,
exlib::string origin, obj_ptr<WebSocket_base>& retVal,
v8::Local<v8::Object> This)
{
Isolate* isolate = Isolate::current();

v8::Local<v8::Object> opts = v8::Object::New(isolate->m_isolate);

opts->Set(isolate->NewString("protocol", 8), isolate->NewString(protocol));
opts->Set(isolate->NewString("origin", 6), isolate->NewString(origin));

return WebSocket_base::_new(url, opts, retVal, This);
}

result_t WebSocket_base::_new(exlib::string url, v8::Local<v8::Object> opts,
obj_ptr<WebSocket_base>& retVal,
v8::Local<v8::Object> This)
{
class asyncConnect : public AsyncState {
public:
asyncConnect(WebSocket* pThis, Isolate* isolate)
asyncConnect(WebSocket* pThis, obj_ptr<NObject> headers, HttpClient_base* hc, Isolate* isolate)
: AsyncState(NULL)
, m_this(pThis)
, m_headers(headers)
, m_hc(hc)
{
m_isolate = isolate;
m_this->isolate_ref();
@@ -206,8 +223,6 @@ result_t WebSocket_base::_new(exlib::string url, exlib::string protocol, exlib::
return CHECK_ERROR(Runtime::setError("websocket: unknown protocol"));
}

pThis->m_headers = new NObject();

pThis->m_headers->add("Upgrade", "websocket");
pThis->m_headers->add("Connection", "Upgrade");
pThis->m_headers->add("Sec-WebSocket-Version", "13");
@@ -239,7 +254,7 @@ result_t WebSocket_base::_new(exlib::string url, exlib::string protocol, exlib::
6, (const char*)output, 20, pThis->m_accept);

pThis->set(response);
return http_request("GET", url, NULL, pThis->m_headers,
return http_request2(pThis->m_hc, "GET", url, NULL, pThis->m_headers,
pThis->m_httprep, pThis);
}

@@ -322,13 +337,29 @@ result_t WebSocket_base::_new(exlib::string url, exlib::string protocol, exlib::
obj_ptr<WebSocket> m_this;
obj_ptr<HttpResponse_base> m_httprep;
obj_ptr<NObject> m_headers;
obj_ptr<HttpClient_base> m_hc;
exlib::string m_accept;
};

Isolate* isolate = Isolate::current();
exlib::string origin = "";
exlib::string protocol = "";
v8::Local<v8::Object> v;
obj_ptr<NObject> headers = new NObject();
obj_ptr<HttpClient_base> hc = NULL;

GetConfigValue(isolate->m_isolate, opts, "protocol", protocol);
GetConfigValue(isolate->m_isolate, opts, "origin", origin);

if (GetConfigValue(isolate->m_isolate, opts, "headers", v) >= 0)
headers->add(v);

GetConfigValue(isolate->m_isolate, opts, "httpClient", hc);

obj_ptr<WebSocket> sock = new WebSocket(url, protocol, origin);
sock->m_holder = new ValueHolder(sock->wrap(This));

(new asyncConnect(sock, sock->holder()))->apost(0);
(new asyncConnect(sock, headers, hc, sock->holder()))->apost(0);

retVal = sock;

@@ -30,9 +30,19 @@ interface WebSocket : EventEmitter
/*! @brief WebSocket 构造函数
@param url 指定连接的服务器
@param protocol 指定握手协议,缺省为 ""
@param origin 指定握手时模拟的源
@param origin 指定握手时模拟的源,缺省为 ""
*/
WebSocket(String url, String protocol = "", String origin = "");

/*! @brief WebSocket 构造函数
@param url 指定连接的服务器
@param opts 连接选项,缺省是 {},支持的字段有 "protocol", "origin", "headers", "httpClient"。
其中 protocol 指定握手协议,缺省为 "",
origin 指定握手时模拟的源,缺省为 "",
headers 是 http(s) 连接时携带的 header 缺省为 {},
httpClient 使用指定 httpClient 实例的 cookie,缺省为 undefined 默认使用全局 cookie,参见 HttpClient
*/
WebSocket(String url, Object opts = {});

/*! @brief 查询当前对象连接的服务器 */
readonly String url;
@@ -476,7 +476,23 @@ describe('ws', () => {

describe('WebSocket', () => {
it("server", () => {
var httpd = new http.Server(8814 + base_port, {
var httpd = new http.Server(8814 + base_port, [(req) => {
var apiToken = req.firstHeader("api-token");
if (apiToken === "valid")
return;
if (apiToken === "invalid") {
req.response.statusCode = 403;
return req.end();
}
}, (req) => {
var sessionId = req.cookies["sessionId"];
if (sessionId === "valid")
return;
if (sessionId === "invalid") {
req.response.statusCode = 403;
return req.end();
}
}, {
"/ws": ws.upgrade((s, req) => {
assert.equal(req.firstHeader("upgrade"), "websocket");
s.onmessage = function (msg) {
@@ -491,8 +507,15 @@ describe('ws', () => {
} else
this.send(msg.data);
};
})
});
}),
"/set-cookie": (req) => {
var json = req.json()
req.response.addCookie({
name: "sessionId",
value: json.value
});
}
}]);
ss.push(httpd.socket);
httpd.run(() => {});
});
@@ -559,6 +582,87 @@ describe('ws', () => {
s.close();
});

it('header authority', () => {
var s = new ws.Socket("ws://127.0.0.1:" + (8814 + base_port) + "/ws", {
protocol: "test",
headers: {
"api-token": "valid"
}
});
assert.equal(s.url, "ws://127.0.0.1:" + (8814 + base_port) + "/ws");
assert.equal(s.protocol, "test");

var opened = false;
s.onopen = () => {
opened = true;
s.close();
};
coroutine.sleep(100);
assert.equal(s.readyState, ws.CLOSED);
assert.isTrue(opened);

var s1 = new ws.Socket("ws://127.0.0.1:" + (8814 + base_port) + "/ws", {
protocol: "test",
headers: {
"api-token": "invalid"
}
});
assert.equal(s1.url, "ws://127.0.0.1:" + (8814 + base_port) + "/ws");
assert.equal(s1.protocol, "test");
opened = false;
s1.onopen = () => {
opened = true;
s1.close();
};
coroutine.sleep(100);
assert.equal(s1.readyState, ws.CLOSED);
assert.isFalse(opened);
});

it("specialize httpClient", () => {
var hc = new http.Client();
hc.post("http://127.0.0.1:" + (8814 + base_port) + "/set-cookie", {
json: {
value: "valid"
}
})
var s = new ws.Socket("ws://127.0.0.1:" + (8814 + base_port) + "/ws", {
protocol: "test",
httpClient: hc
});
assert.equal(s.url, "ws://127.0.0.1:" + (8814 + base_port) + "/ws");
assert.equal(s.protocol, "test");

var opened = false;
s.onopen = () => {
opened = true;
s.close();
};
coroutine.sleep(100);
assert.equal(s.readyState, ws.CLOSED);
assert.isTrue(opened);

hc.post("http://127.0.0.1:" + (8814 + base_port) + "/set-cookie", {
json: {
value: "invalid"
}
})
var s1 = new ws.Socket("ws://127.0.0.1:" + (8814 + base_port) + "/ws", {
protocol: "test",
httpClient: hc
});
assert.equal(s1.url, "ws://127.0.0.1:" + (8814 + base_port) + "/ws");
assert.equal(s1.protocol, "test");
var opened1 = false;
s1.onopen = () => {
opened1 = true;
s1.close();
};
coroutine.sleep(100);
assert.equal(s1.readyState, ws.CLOSED);
assert.isFalse(opened1);
})

it('many compressed message', () => {
var cnt = 0;
var sz = 0;

0 comments on commit 70ff419

Please sign in to comment.
You can’t perform that action at this time.