| #include "util.h" |
| |
| #ifdef HAVE_LTTNG |
| #include "node_lttng.h" |
| #include "node_lttng_provider.h" |
| #include <string.h> |
| #else |
| #define NODE_HTTP_SERVER_REQUEST(arg0, arg1) |
| #define NODE_HTTP_SERVER_REQUEST_ENABLED() (0) |
| #define NODE_HTTP_SERVER_RESPONSE(arg0) |
| #define NODE_HTTP_SERVER_RESPONSE_ENABLED() (0) |
| #define NODE_HTTP_CLIENT_REQUEST(arg0, arg1) |
| #define NODE_HTTP_CLIENT_REQUEST_ENABLED() (0) |
| #define NODE_HTTP_CLIENT_RESPONSE(arg0) |
| #define NODE_HTTP_CLIENT_RESPONSE_ENABLED() (0) |
| #define NODE_NET_SERVER_CONNECTION(arg0) |
| #define NODE_NET_SERVER_CONNECTION_ENABLED() (0) |
| #define NODE_NET_STREAM_END(arg0) |
| #define NODE_NET_STREAM_END_ENABLED() (0) |
| #define NODE_GC_START(arg0, arg1, arg2) |
| #define NODE_GC_DONE(arg0, arg1, arg2) |
| #endif |
| |
| #include "env.h" |
| #include "env-inl.h" |
| |
| namespace node { |
| |
| using v8::FunctionCallbackInfo; |
| using v8::FunctionTemplate; |
| using v8::GCCallbackFlags; |
| using v8::GCEpilogueCallback; |
| using v8::GCPrologueCallback; |
| using v8::GCType; |
| using v8::Handle; |
| using v8::HandleScope; |
| using v8::Isolate; |
| using v8::Local; |
| using v8::Object; |
| using v8::String; |
| using v8::Value; |
| |
| #define SLURP_STRING(obj, member, valp) \ |
| if (!(obj)->IsObject()) { \ |
| return env->ThrowError( \ |
| "expected object for " #obj " to contain string member " #member); \ |
| } \ |
| node::Utf8Value _##member(env->isolate(), \ |
| obj->Get(OneByteString(env->isolate(), #member))); \ |
| if ((*(const char **)valp = *_##member) == nullptr) \ |
| *(const char **)valp = "<unknown>"; |
| |
| #define SLURP_INT(obj, member, valp) \ |
| if (!(obj)->IsObject()) { \ |
| return env->ThrowError( \ |
| "expected object for " #obj " to contain integer member " #member); \ |
| } \ |
| *valp = obj->Get(OneByteString(env->isolate(), #member)) \ |
| ->ToInteger(env->isolate())->Value(); |
| |
| #define SLURP_OBJECT(obj, member, valp) \ |
| if (!(obj)->IsObject()) { \ |
| return env->ThrowError( \ |
| "expected object for " #obj " to contain object member " #member); \ |
| } \ |
| *valp = Local<Object>::Cast(obj->Get(OneByteString(env->isolate(), #member))); |
| |
| #define SLURP_CONNECTION(arg, conn) \ |
| if (!(arg)->IsObject()) { \ |
| return env->ThrowError( \ |
| "expected argument " #arg " to be a connection object"); \ |
| } \ |
| node_lttng_connection_t conn; \ |
| Local<Object> _##conn = Local<Object>::Cast(arg); \ |
| Local<Value> _handle = \ |
| (_##conn)->Get(FIXED_ONE_BYTE_STRING(env->isolate(), "_handle")); \ |
| if (_handle->IsObject()) { \ |
| SLURP_INT(_handle.As<Object>(), fd, &conn.fd); \ |
| } else { \ |
| conn.fd = -1; \ |
| } \ |
| SLURP_STRING(_##conn, remoteAddress, &conn.remote); \ |
| SLURP_INT(_##conn, remotePort, &conn.port); \ |
| SLURP_INT(_##conn, bufferSize, &conn.buffered); |
| |
| #define SLURP_CONNECTION_HTTP_CLIENT(arg, conn) \ |
| if (!(arg)->IsObject()) { \ |
| return env->ThrowError( \ |
| "expected argument " #arg " to be a connection object"); \ |
| } \ |
| node_lttng_connection_t conn; \ |
| Local<Object> _##conn = Local<Object>::Cast(arg); \ |
| SLURP_INT(_##conn, fd, &conn.fd); \ |
| SLURP_STRING(_##conn, host, &conn.remote); \ |
| SLURP_INT(_##conn, port, &conn.port); \ |
| SLURP_INT(_##conn, bufferSize, &conn.buffered); |
| |
| #define SLURP_CONNECTION_HTTP_CLIENT_RESPONSE(arg0, arg1, conn) \ |
| if (!(arg0)->IsObject()) { \ |
| return env->ThrowError( \ |
| "expected argument " #arg0 " to be a connection object"); \ |
| } \ |
| if (!(arg1)->IsObject()) { \ |
| return env->ThrowError( \ |
| "expected argument " #arg1 " to be a connection object"); \ |
| } \ |
| node_lttng_connection_t conn; \ |
| Local<Object> _##conn = Local<Object>::Cast(arg0); \ |
| SLURP_INT(_##conn, fd, &conn.fd); \ |
| SLURP_INT(_##conn, bufferSize, &conn.buffered); \ |
| _##conn = Local<Object>::Cast(arg1); \ |
| SLURP_STRING(_##conn, host, &conn.remote); \ |
| SLURP_INT(_##conn, port, &conn.port); |
| |
| |
| void LTTNG_NET_SERVER_CONNECTION(const FunctionCallbackInfo<Value>& args) { |
| if (!NODE_NET_SERVER_CONNECTION_ENABLED()) |
| return; |
| Environment* env = Environment::GetCurrent(args); |
| SLURP_CONNECTION(args[0], conn); |
| NODE_NET_SERVER_CONNECTION(&conn, conn.remote, conn.port, conn.fd); |
| } |
| |
| |
| void LTTNG_NET_STREAM_END(const FunctionCallbackInfo<Value>& args) { |
| if (!NODE_NET_STREAM_END_ENABLED()) |
| return; |
| Environment* env = Environment::GetCurrent(args); |
| SLURP_CONNECTION(args[0], conn); |
| NODE_NET_STREAM_END(&conn, conn.remote, conn.port, conn.fd); |
| } |
| |
| |
| void LTTNG_HTTP_SERVER_REQUEST(const FunctionCallbackInfo<Value>& args) { |
| node_lttng_http_server_request_t req; |
| |
| if (!NODE_HTTP_SERVER_REQUEST_ENABLED()) |
| return; |
| |
| if (!args[0]->IsObject()) |
| return; |
| |
| Environment* env = Environment::GetCurrent(args); |
| Local<Object> arg0 = args[0].As<Object>(); |
| Local<Object> headers; |
| |
| memset(&req, 0, sizeof(req)); |
| req._un.version = 1; |
| SLURP_STRING(arg0, url, &req.url); |
| SLURP_STRING(arg0, method, &req.method); |
| SLURP_OBJECT(arg0, headers, &headers); |
| |
| if (!(headers)->IsObject()) { |
| return env->ThrowError( |
| "expected object for request to contain string member headers"); |
| } |
| |
| Local<Value> strfwdfor = headers->Get(env->x_forwarded_string()); |
| node::Utf8Value fwdfor(env->isolate(), strfwdfor); |
| req.forwarded_for = *fwdfor; |
| |
| if (!strfwdfor->IsString() || req.forwarded_for == nullptr) |
| req.forwarded_for = ""; |
| |
| SLURP_CONNECTION(args[1], conn); |
| NODE_HTTP_SERVER_REQUEST(&req, &conn, conn.remote, conn.port, req.method, \ |
| req.url, conn.fd); |
| } |
| |
| |
| void LTTNG_HTTP_SERVER_RESPONSE(const FunctionCallbackInfo<Value>& args) { |
| if (!NODE_HTTP_SERVER_RESPONSE_ENABLED()) |
| return; |
| Environment* env = Environment::GetCurrent(args); |
| SLURP_CONNECTION(args[0], conn); |
| NODE_HTTP_SERVER_RESPONSE(&conn, conn.remote, conn.port, conn.fd); |
| } |
| |
| |
| void LTTNG_HTTP_CLIENT_REQUEST(const FunctionCallbackInfo<Value>& args) { |
| node_lttng_http_client_request_t req; |
| char* header; |
| |
| if (!NODE_HTTP_CLIENT_REQUEST_ENABLED()) |
| return; |
| |
| Environment* env = Environment::GetCurrent(args); |
| |
| /* |
| * For the method and URL, we're going to dig them out of the header. This |
| * is not as efficient as it could be, but we would rather not force the |
| * caller here to retain their method and URL until the time at which |
| * LTTNG_HTTP_CLIENT_REQUEST can be called. |
| */ |
| Local<Object> arg0 = args[0].As<Object>(); |
| SLURP_STRING(arg0, _header, &header); |
| |
| req.method = header; |
| |
| while (*header != '\0' && *header != ' ') |
| header++; |
| |
| if (*header != '\0') |
| *header++ = '\0'; |
| |
| req.url = header; |
| |
| while (*header != '\0' && *header != ' ') |
| header++; |
| |
| *header = '\0'; |
| |
| SLURP_CONNECTION_HTTP_CLIENT(args[1], conn); |
| NODE_HTTP_CLIENT_REQUEST(&req, &conn, conn.remote, conn.port, req.method, \ |
| req.url, conn.fd); |
| } |
| |
| |
| void LTTNG_HTTP_CLIENT_RESPONSE(const FunctionCallbackInfo<Value>& args) { |
| if (!NODE_HTTP_CLIENT_RESPONSE_ENABLED()) |
| return; |
| Environment* env = Environment::GetCurrent(args); |
| SLURP_CONNECTION_HTTP_CLIENT_RESPONSE(args[0], args[1], conn); |
| NODE_HTTP_CLIENT_RESPONSE(&conn, conn.remote, conn.port, conn.fd); |
| } |
| |
| |
| void lttng_gc_start(Isolate* isolate, GCType type, GCCallbackFlags flags) { |
| NODE_GC_START(type, flags, isolate); |
| } |
| |
| |
| void lttng_gc_done(Isolate* isolate, GCType type, GCCallbackFlags flags) { |
| NODE_GC_DONE(type, flags, isolate); |
| } |
| |
| void InitLTTNG(Environment* env, Handle<Object> target) { |
| HandleScope scope(env->isolate()); |
| |
| static struct { |
| const char *name; |
| void (*func)(const FunctionCallbackInfo<Value>&); |
| } tab[] = { |
| #define NODE_PROBE(name) #name, name |
| { NODE_PROBE(LTTNG_NET_SERVER_CONNECTION) }, |
| { NODE_PROBE(LTTNG_NET_STREAM_END) }, |
| { NODE_PROBE(LTTNG_HTTP_SERVER_REQUEST) }, |
| { NODE_PROBE(LTTNG_HTTP_SERVER_RESPONSE) }, |
| { NODE_PROBE(LTTNG_HTTP_CLIENT_REQUEST) }, |
| { NODE_PROBE(LTTNG_HTTP_CLIENT_RESPONSE) } |
| #undef NODE_PROBE |
| }; |
| |
| for (unsigned int i = 0; i < ARRAY_SIZE(tab); i++) { |
| Local<String> key = OneByteString(env->isolate(), tab[i].name); |
| Local<Value> val = env->NewFunctionTemplate(tab[i].func)->GetFunction(); |
| target->Set(key, val); |
| } |
| |
| #if defined HAVE_LTTNG |
| env->isolate()->AddGCPrologueCallback(lttng_gc_start); |
| env->isolate()->AddGCEpilogueCallback(lttng_gc_done); |
| #endif |
| } |
| |
| } // namespace node |