mirror of
https://github.com/Ylianst/MeshAgent
synced 2025-12-06 00:13:33 +00:00
5117 lines
229 KiB
C
5117 lines
229 KiB
C
/*
|
|
Copyright 2006 - 2022 Intel Corporation
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software2
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
#ifdef WIN32
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
|
|
#include <Windows.h>
|
|
#include <WinBase.h>
|
|
#endif
|
|
|
|
#include "../microstack/ILibParsers.h"
|
|
|
|
#include "duktape.h"
|
|
#include "ILibDuktape_Helpers.h"
|
|
#include "ILibDuktapeModSearch.h"
|
|
#include "ILibDuktape_EventEmitter.h"
|
|
#include "ILibDuktape_DuplexStream.h"
|
|
#include "ILibDuktape_net.h"
|
|
|
|
#include "../microstack/ILibWebClient.h"
|
|
#include "../microstack/ILibRemoteLogging.h"
|
|
|
|
struct ILibWebClientDataObject;
|
|
extern int ILibWebServer_WebSocket_CreateHeader(char* header, unsigned short FLAGS, unsigned short OPCODE, int payloadLength);
|
|
extern void ILibWebClient_ResetWCDO(struct ILibWebClientDataObject *wcdo);
|
|
extern int ILibDeflate(char *buffer, size_t bufferLen, char *compressed, size_t *compressedLen, uint32_t *crc);
|
|
void ILibDuktape_RemoveObjFromTable(duk_context *ctx, duk_idx_t tableIdx, char *key, void *obj);
|
|
|
|
#define ILibDuktape_Agent_SocketJustCreated "\xFF_Agent_SocketJustCreated"
|
|
#define ILibDuktape_ClientRequest "\xFF_CR"
|
|
#define ILibDuktape_CR_AbortCalled "\xFF_CR_AbortCalled"
|
|
#define ILibDuktape_CR_EndCalled "\xFF_CR_EndCalled"
|
|
#define ILibDuktape_CR_RequestBuffer "\xFF_CR_RequestBuffer"
|
|
#define ILibDuktape_CR2Agent "\xFF_CR2Agent"
|
|
#define ILibDuktape_CR2HTTPStream "\xFF_CR2HTTPStream"
|
|
#define ILibDuktape_CR2TLSStream "\xFF_CR2TLSStream"
|
|
#define ILibDuktape_CR2Transform "\xFF_CR2Transform"
|
|
#define ILibDuktape_FUNC "\xFF_FUNC"
|
|
#define IILibDuktape_HTTP_HoldingQueue "\xFF_HoldingQueue"
|
|
#define ILibDuktape_Http_Server_FixedBuffer "\xFF_Http_Server_FixedBuffer"
|
|
#define ILibDuktape_Http_Server2NetServer "\xFF_HttpServer2NetServer"
|
|
#define ILibduktape_HttpStream2HttpServer "\xFF_HttpStream_2_HttpServer"
|
|
#define ILibDuktape_HttpServer_TimeoutCB "\xFF_HttpServer_TimeoutCB"
|
|
#define ILibDuktape_HTTP2CR "\xFF_HTTP2ClientRequest"
|
|
#define ILibDuktape_HTTP2PipedReadable "\xFF_HTTP2PipedReadable"
|
|
#define ILibDuktape_HTTP2PipedWritable "\xFF_HTTP2PipedWritable"
|
|
#define ILibDuktape_HTTPStream2Data "\xFF_HTTPStream2Data"
|
|
#define ILibDuktape_HTTPStream2HTTP "\xFF_HTTPStream2HTTP"
|
|
//#define ILibDuktape_HTTPStream2IMSG "\xFF_HTTPStream2IMSG"
|
|
#define ILibDuktape_HTTPStream2Socket "\xFF_HTTPStream2Socket"
|
|
#define ILibDuktape_IMSG2HttpStream "\xFF_IMSG2HttpStream"
|
|
#define ILibDuktape_IMSG2Ptr "\xFF_IMSG2Ptr"
|
|
#define ILibDuktape_IMSG2SR "\xFF_IMSG2ServerResponse"
|
|
#define ILibDuktape_NS2HttpServer "\xFF_Http_NetServer2HttpServer"
|
|
#define ILibDuktape_Options2ClientRequest "\xFF_Options2ClientRequest"
|
|
#define ILibDuktape_RawOptionsBuffer "\xFF_RawOptionsBuffer"
|
|
#define ILibDuktape_Socket2AgentStash "\xFF_Socket2AgentStash"
|
|
#define ILibDuktape_Socket2Agent "\xFF_Socket2Agent"
|
|
#define ILibDuktape_Socket2AgentKey "\xFF_Socket2AgentKey"
|
|
#define ILibDuktape_Socket2CR "\xFF_Socket2CR"
|
|
#define ILibDuktape_Socket2CR_Abort "\xFF_Socket2CR_Abort"
|
|
#define ILibDuktape_Socket2HttpServer "\xFF_Socket2HttpServer"
|
|
#define ILibDuktape_Socket2HttpStream "\xFF_Socket2HttpStream"
|
|
#define ILibDuktape_Socket2DiedListener "\xFF_Socket2DiedListener"
|
|
#define ILibDuktape_Socket2TLS "\xFF_Socket2TLS"
|
|
#define ILibDuktape_SR2HttpStream "\xFF_ServerResponse2HttpStream"
|
|
#define ILibDuktape_SR2ImplicitHeaders "\xFF_ServerResponse2ImplicitHeaders"
|
|
#define ILibDuktape_SR2State "\xFF_ServerResponse2State"
|
|
#define ILibDuktape_SRUSER "\xFF_SRUSER"
|
|
#define ILibDuktape_SR2WS "\xFF_Http_ServerResponse2WS"
|
|
#define ILibDuktape_WebSocket_Client ((void*)0x01)
|
|
#define ILibDuktape_WebSocket_Server ((void*)0x02)
|
|
#define ILibDuktape_WebSocket_StatePtr "\xFF_WebSocketState"
|
|
#define ILibDuktape_WebSocket_Decompressor "\xFF_WebSocket_Decompressor"
|
|
#define ILibDuktape_WSENC2WS "\xFF_WSENC2WS"
|
|
#define ILibDuktape_WS2CR "\xFF_WS2ClientRequest"
|
|
#define ILibDuktape_WSDEC2WS "\xFF_WSDEC2WS"
|
|
|
|
extern void ILibWebServer_Digest_ParseAuthenticationHeader(void* table, char* value, int valueLen);
|
|
extern int *ILibWebClient_WCDO_ServerFlag(ILibWebClient_StateObject j);
|
|
void ILibDuktape_HttpStream_ServerResponse_PUSH(duk_context *ctx, void* writeStream, ILibHTTPPacket *header, void *httpStream);
|
|
|
|
typedef struct ILibDuktape_Http_ClientRequest_WriteData
|
|
{
|
|
char *buffer;
|
|
int noMoreWrites;
|
|
int headersFinished;
|
|
int contentLengthSpecified;
|
|
int needRetry;
|
|
int retryCounter;
|
|
size_t bufferWriteLen;
|
|
size_t bufferLen;
|
|
}ILibDuktape_Http_ClientRequest_WriteData;
|
|
|
|
typedef struct ILibDuktape_HttpStream_Data
|
|
{
|
|
ILibDuktape_DuplexStream *DS;
|
|
ILibDuktape_readableStream *bodyStream;
|
|
|
|
int bodyStream_unshiftedBytes;
|
|
|
|
void *DynamicBuffer;
|
|
int connectionCloseSpecified;
|
|
int contentLengthSpecified;
|
|
void *WCDO;
|
|
void *chain;
|
|
int ConnectMethod;
|
|
int endPropagated;
|
|
int maxHeaderSize;
|
|
|
|
char entityHash[MD5_DIGEST_LENGTH];
|
|
MD5_CTX entityHashCtx;
|
|
}ILibDuktape_HttpStream_Data;
|
|
|
|
typedef struct ILibDuktape_HttpStream_ServerResponse_State
|
|
{
|
|
duk_context *ctx;
|
|
void *chain;
|
|
void *writeStream;
|
|
void *serverResponse;
|
|
ILibDuktape_WritableStream *nativeWriteStream;
|
|
int implicitHeaderHandling;
|
|
int chunkSupported;
|
|
int contentLengthSpecified;
|
|
}ILibDuktape_HttpStream_ServerResponse_State;
|
|
typedef struct ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State
|
|
{
|
|
void *ctx;
|
|
void *writeStream;
|
|
void *serverResponseObj;
|
|
void *serverResponseStream;
|
|
int endBytes;
|
|
int chunk;
|
|
size_t bufferLen;
|
|
char buffer[];
|
|
}ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State;
|
|
|
|
typedef struct ILibDuktape_WebSocket_State
|
|
{
|
|
void *chain;
|
|
int noMasking;
|
|
int WebSocketFragmentFlag; // WebSocketFragmentFlag
|
|
int WebSocketFragmentFlag_Write; // WebSocketFragmentFlag
|
|
int WebSocketDataFrameType; // WebSocketDataFrameType
|
|
char* WebSocketFragmentBuffer; // WebSocketFragmentBuffer
|
|
int WebSocketFragmentIndex; // WebSocketFragmentIndex;
|
|
int WebSocketFragmentBufferSize; // WebSocketFragmentBufferSize;
|
|
int WebSocketFragmentMaxBufferSize; // WebSocketFragmentMaxBufferSize;
|
|
char WebSocketCloseFrameSent; // WebSocketCloseFrameSent
|
|
void *ObjectPtr; // Used to emit Ping/Pong events
|
|
duk_context *ctx; // Used to emit Ping/Pong events
|
|
|
|
int noResume;
|
|
int closed;
|
|
int permessageDeflate;
|
|
int minimumThreshold;
|
|
int maxSkipCount;
|
|
int minSkipCount;
|
|
int skipCount;
|
|
int nonCompressibleCount;
|
|
int sWBITS, cWBITS;
|
|
|
|
uint64_t uncompressedSent, uncompressedReceived;
|
|
uint64_t actualSent, actualReceived;
|
|
|
|
ILibDuktape_DuplexStream *encodedStream;
|
|
ILibDuktape_DuplexStream *decodedStream;
|
|
}ILibDuktape_WebSocket_State;
|
|
|
|
typedef struct ILibDuktape_Http_Server
|
|
{
|
|
duk_context *ctx;
|
|
|
|
}ILibDuktape_Http_Server;
|
|
|
|
typedef struct ILibDuktape_http_ServerResponse
|
|
{
|
|
duk_context *ctx;
|
|
ILibDuktape_WritableStream *ws;
|
|
|
|
}ILibDuktape_http_ServerResponse;
|
|
|
|
int ILibDuktape_Headers_IsChunkSupported(ILibHTTPPacket *header)
|
|
{
|
|
if (header->VersionLength == 3 && strncmp(header->Version, "1.0", 3) == 0)
|
|
{
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
return(1);
|
|
}
|
|
}
|
|
void ILibDuktape_serverResponse_resetHttpStream(duk_context *ctx, void *serverResponse)
|
|
{
|
|
duk_idx_t t = duk_get_top(ctx);
|
|
|
|
// Need to reset HttpStream
|
|
duk_push_heapptr(ctx, serverResponse); // [serverResponse]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_SR2HttpStream); // [serverResponse][httpStream]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2Data); // [serverResponse][httpStream][data]
|
|
ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)Duktape_GetBuffer(ctx, -1, NULL);
|
|
|
|
ILibWebClient_FinishedResponse_Server(data->WCDO);
|
|
if (data->bodyStream != NULL)
|
|
{
|
|
ILibDuktape_readableStream_WriteEnd(data->bodyStream);
|
|
data->bodyStream = NULL;
|
|
}
|
|
|
|
duk_set_top(ctx, t);
|
|
}
|
|
void ILibDuktape_Digest_CalculateNonce(duk_context *ctx, void *heapptr, long long expiration, char *opaque, int opaqueLen, char* buffer)
|
|
{
|
|
char temp[33];
|
|
if (expiration == 0)
|
|
{
|
|
if (opaqueLen != 16) { ILibDuktape_Error(ctx, "Invalid opaque specified"); return; }
|
|
char tmp[8];
|
|
util_hexToBuf(opaque, opaqueLen, tmp);
|
|
expiration = ((long long*)tmp)[0];
|
|
}
|
|
memcpy_s(temp, sizeof(temp), &expiration, 8);
|
|
memcpy_s(temp + 8, sizeof(temp) - 8, &heapptr, sizeof(void*));
|
|
|
|
util_md5hex(temp, 8 + sizeof(void*), buffer);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_get(duk_context *ctx)
|
|
{
|
|
int nargs = duk_get_top(ctx);
|
|
if (duk_is_string(ctx, 0))
|
|
{
|
|
// First Param is a string
|
|
duk_push_this(ctx); // [http]
|
|
duk_get_prop_string(ctx, -1, "parseUri"); // [http][parseUri]
|
|
duk_swap_top(ctx, -2); // [parseUri][this]
|
|
duk_dup(ctx, 0); // [parseUri][this][uri]
|
|
duk_call_method(ctx, 1); // [uri]
|
|
duk_push_this(ctx); // [uri][http]
|
|
duk_get_prop_string(ctx, -1, "request"); // [uri][http][request]
|
|
duk_swap_top(ctx, -2); // [uri][request][this]
|
|
duk_push_object(ctx); // [uri][request][this][options]
|
|
duk_get_prop_string(ctx, -4, "protocol"); // [uri][request][this][options][protocol]
|
|
duk_put_prop_string(ctx, -2, "protocol"); // [uri][request][this][options]
|
|
duk_get_prop_string(ctx, -4, "host"); // [uri][request][this][options][host]
|
|
duk_put_prop_string(ctx, -2, "host"); // [uri][request][this][options]
|
|
duk_get_prop_string(ctx, -4, "port"); // [uri][request][this][options][port]
|
|
duk_put_prop_string(ctx, -2, "port"); // [uri][request][this][options]
|
|
duk_get_prop_string(ctx, -4, "path"); // [uri][request][this][options][path]
|
|
duk_put_prop_string(ctx, -2, "path"); // [uri][request][this][options]
|
|
duk_push_string(ctx, "GET"); // [uri][request][this][options][method]
|
|
duk_put_prop_string(ctx, -2, "method"); // [uri][request][this][options]
|
|
}
|
|
else if (duk_is_object(ctx, 0))
|
|
{
|
|
duk_push_this(ctx); // [http]
|
|
duk_get_prop_string(ctx, -1, "request"); // [http][request]
|
|
duk_swap_top(ctx, -2); // [request][this]
|
|
duk_dup(ctx, 0); // [request][this][options]
|
|
duk_push_string(ctx, "GET"); // [request][this][options][method]
|
|
duk_put_prop_string(ctx, -2, "method"); // [request][this][options]
|
|
}
|
|
else
|
|
{
|
|
return(ILibDuktape_Error(ctx, "http.get(): invalid parameter type"));
|
|
}
|
|
|
|
if (nargs > 1 && duk_is_function(ctx, 1))
|
|
{
|
|
duk_dup(ctx, 1); // [request][this][options][callback]
|
|
duk_call_method(ctx, 2); // [retVal]
|
|
}
|
|
else
|
|
{ // [request][this][options]
|
|
duk_call_method(ctx, 1); // [retVal]
|
|
}
|
|
|
|
// [clientRequest]
|
|
duk_get_prop_string(ctx, -1, "end"); // [clientRequest][end]
|
|
duk_dup(ctx, -2); // [clientRequest][end][this]
|
|
duk_call_method(ctx, 0); duk_pop(ctx); // [clientRequest]
|
|
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_checkIdentity(duk_context *ctx)
|
|
{
|
|
int nargs = duk_get_top(ctx);
|
|
int i;
|
|
|
|
duk_push_current_function(ctx);
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_FUNC); // [func]
|
|
duk_get_prop_string(ctx, -2, ILibDuktape_ClientRequest); // [func][this]
|
|
for (i = 0; i < nargs; ++i)
|
|
{
|
|
duk_dup(ctx, i); // [func][this][...args]
|
|
}
|
|
duk_call_method(ctx, nargs);
|
|
return(1);
|
|
}
|
|
|
|
void ILibDuktape_HttpStream_http_ConvertOptionToSend(duk_context *ctx, void *ObjectPtr, void *OptionsPtr)
|
|
{
|
|
char *tmp, *buffer = NULL;
|
|
duk_size_t len;
|
|
size_t bufferLen = 0;
|
|
int i;
|
|
int expectSpecified = 0;
|
|
ILibDuktape_Http_ClientRequest_WriteData *data;
|
|
|
|
duk_push_heapptr(ctx, ObjectPtr); // [stream]
|
|
duk_push_heapptr(ctx, OptionsPtr); // [stream][Options]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Options2ClientRequest); // [stream][Options][CR]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_CR_RequestBuffer); // [stream][Options][CR][data]
|
|
data = (ILibDuktape_Http_ClientRequest_WriteData*)Duktape_GetBuffer(ctx, -1, NULL);
|
|
duk_pop_2(ctx); // [stream][Options]
|
|
|
|
char *protocol = Duktape_GetStringPropertyValue(ctx, -1, "protocol", "");
|
|
if (strcmp(protocol, "ws:") == 0 || strcmp(protocol, "wss:") == 0)
|
|
{
|
|
int permessagedeflate = Duktape_GetBooleanProperty(ctx, -1, "perMessageDeflate", 0);
|
|
if (duk_has_prop_string(ctx, -1, "headers"))
|
|
{
|
|
duk_get_prop_string(ctx, -1, "headers"); // [stream][Options][headers]
|
|
}
|
|
else
|
|
{
|
|
duk_push_object(ctx); // [stream][Options][headers]
|
|
duk_dup(ctx, -1); // [stream][Options][headers][dup]
|
|
duk_put_prop_string(ctx, -3, "headers"); // [stream][Options][headers]
|
|
}
|
|
char nonce[16];
|
|
char value[26];
|
|
char *enc = value;
|
|
|
|
util_random(16, nonce);
|
|
ILibBase64Encode((unsigned char*)nonce, 16, (unsigned char**)&enc);
|
|
|
|
duk_push_string(ctx, "websocket");
|
|
duk_put_prop_string(ctx, -2, "Upgrade");
|
|
|
|
duk_push_string(ctx, "Upgrade");
|
|
duk_put_prop_string(ctx, -2, "Connection");
|
|
|
|
duk_push_string(ctx, enc);
|
|
duk_put_prop_string(ctx, -2, "Sec-WebSocket-Key");
|
|
|
|
duk_push_string(ctx, "13");
|
|
duk_put_prop_string(ctx, -2, "Sec-WebSocket-Version");
|
|
|
|
if (permessagedeflate != 0)
|
|
{
|
|
duk_push_string(ctx, "permessage-deflate; server_no_context_takeover; client_no_context_takeover");
|
|
duk_put_prop_string(ctx, -2, "Sec-WebSocket-Extensions");
|
|
}
|
|
duk_pop(ctx); // [stream][options]
|
|
}
|
|
|
|
for (i = 0; i < 2; ++i)
|
|
{
|
|
// measure how big a buffer we'll need
|
|
duk_get_prop_string(ctx, -1, "method"); // [stream][options][method]
|
|
tmp = (char*)duk_get_lstring(ctx, -1, &len);
|
|
if (buffer != NULL) { memcpy_s(buffer + bufferLen, ILibMemory_AllocateA_Size(buffer) - bufferLen, tmp, len); (buffer + bufferLen)[len] = ' '; }
|
|
bufferLen += (len + 1); // ('GET ')
|
|
duk_pop(ctx); // [stream][options]
|
|
|
|
duk_get_prop_string(ctx, -1, "path"); // [stream][options][path]
|
|
tmp = (char*)duk_get_lstring(ctx, -1, &len);
|
|
if (buffer != NULL)
|
|
{
|
|
memcpy_s(buffer + bufferLen, ILibMemory_AllocateA_Size(buffer), tmp, len);
|
|
memcpy_s(buffer + bufferLen + len, ILibMemory_AllocateA_Size(buffer) - bufferLen - len, " HTTP/1.1\r\n", 11);
|
|
}
|
|
bufferLen += (len + 11); // ('/path HTTP/1.1\r\n')
|
|
duk_pop(ctx); // [stream][options]
|
|
|
|
if (duk_has_prop_string(ctx, -1, "headers"))
|
|
{
|
|
duk_get_prop_string(ctx, -1, "headers"); // [stream][options][headers]
|
|
duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); // [stream][options][headers][enumerator]
|
|
while (duk_next(ctx, -1, 1))
|
|
{
|
|
tmp = (char*)duk_get_lstring(ctx, -2, &len);
|
|
if (buffer != NULL) { memcpy_s(buffer + bufferLen, ILibMemory_AllocateA_Size(buffer) - bufferLen, tmp, len); (buffer + bufferLen)[len] = ':'; (buffer + bufferLen)[len + 1] = ' '; }
|
|
if (len == 6 && strncasecmp(tmp, "expect", 6) == 0) { expectSpecified = 1; }
|
|
if (len == 14 && strncasecmp(tmp, "content-length", 14) == 0) { data->contentLengthSpecified = 1; }
|
|
bufferLen += (len + 2); // ('key: ')
|
|
tmp = (char*)duk_get_lstring(ctx, -1, &len);
|
|
if (buffer != NULL) { memcpy_s(buffer + bufferLen, ILibMemory_AllocateA_Size(buffer) - bufferLen, tmp, len); (buffer + bufferLen)[len] = '\r'; (buffer + bufferLen)[len + 1] = '\n'; }
|
|
bufferLen += (len + 2); // ('value\r\n')
|
|
duk_pop_2(ctx); // [stream][options][headers][enumerator]
|
|
}
|
|
duk_pop_2(ctx); // [stream][options]
|
|
}
|
|
if (expectSpecified)
|
|
{
|
|
if (buffer != NULL) { buffer[bufferLen] = '\r'; buffer[bufferLen + 1] = '\n'; }
|
|
bufferLen += 2; // (\r\n')
|
|
}
|
|
if (buffer == NULL)
|
|
{
|
|
if (bufferLen > 16384) { ILibDuktape_Error(ctx, "option data too large"); return; }
|
|
buffer = ILibMemory_AllocateA(bufferLen);
|
|
bufferLen = 0;
|
|
}
|
|
}
|
|
|
|
if (expectSpecified)
|
|
{
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Options2ClientRequest); // [stream][options][clientRequest]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_CR_RequestBuffer); // [stream][options][clientRequest][buffer]
|
|
((ILibDuktape_Http_ClientRequest_WriteData*)Duktape_GetBuffer(ctx, -1, NULL))->headersFinished = 1;
|
|
duk_pop_2(ctx); // [stream][options]
|
|
}
|
|
|
|
duk_push_external_buffer(ctx); // [stream][options][extBuffer]
|
|
duk_config_buffer(ctx, -1, buffer, bufferLen); // [stream][options][extBuffer]
|
|
duk_dup(ctx, -3); // [stream][options][extBuffer][stream]
|
|
duk_get_prop_string(ctx, -1, "write"); // [stream][options][extBuffer][stream][write]
|
|
duk_swap_top(ctx, -2); // [stream][options][extBuffer][write][this]
|
|
duk_push_buffer_object(ctx, -3, 0, bufferLen, DUK_BUFOBJ_NODEJS_BUFFER);// [stream][options][extBuffer][write][this][buffer]
|
|
|
|
if (duk_pcall_method(ctx, 1) != 0) // [stream][options][extBuffer][retVal]
|
|
{
|
|
ILibDuktape_Error(ctx, "http.onConnect(): %s", duk_safe_to_string(ctx, -1));
|
|
}
|
|
|
|
duk_pop_n(ctx, 4); // ...
|
|
}
|
|
|
|
duk_ret_t ILibDuktape_HttpStream_http_WebSocket_closed(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [socket]
|
|
if (duk_has_prop_string(ctx, -1, ILibDuktape_Socket2CR))
|
|
{
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Socket2CR); // [socket][CR]
|
|
duk_push_undefined(ctx); // [socket][CR][undefined]
|
|
ILibDuktape_CreateReadonlyProperty(ctx, "socket"); // [socket][CR]
|
|
duk_pop(ctx); // [socket]
|
|
duk_del_prop_string(ctx, -1, ILibDuktape_Socket2CR);
|
|
}
|
|
duk_get_prop_string(ctx, -1, "unpipe"); // [socket][unpipe]
|
|
duk_swap_top(ctx, -2); // [unpipe][this]
|
|
duk_call_method(ctx, 0);
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_onUpgrade(duk_context *ctx)
|
|
{
|
|
char *decodedKey;
|
|
duk_size_t decodedKeyLen;
|
|
char *key;
|
|
duk_size_t keyLen;
|
|
int permessageDeflate = 0;
|
|
int smwb = 15;
|
|
int cmwb = 15;
|
|
|
|
duk_get_prop_string(ctx, 0, "headers"); // [headers]
|
|
duk_get_prop_string(ctx, -1, "Sec-WebSocket-Accept"); // [headers][key]
|
|
key = (char*)Duktape_GetBuffer(ctx, -1, &keyLen);
|
|
if (keyLen > 255) { keyLen = 255; }
|
|
|
|
if (duk_has_prop_string(ctx, -2, "Sec-WebSocket-Extensions"))
|
|
{
|
|
duk_get_prop_string(ctx, -2, "Sec-WebSocket-Extensions"); // [headers][key][extensions]
|
|
duk_string_split(ctx, -1, ";"); // [headers][key][extensions][array]
|
|
while (duk_get_length(ctx, -1) > 0)
|
|
{
|
|
duk_array_pop(ctx, -1); // [headers][key][extensions][array][string]
|
|
duk_string_split(ctx, -1, "="); // [headers][key][extensions][array][string][array]
|
|
duk_array_shift(ctx, -1); // [headers][key][extensions][array][string][array][val1]
|
|
if (strcmp("permessage-deflate", duk_to_string(ctx, -1)) == 0) { permessageDeflate = 1; duk_pop_3(ctx); }
|
|
else if (strcmp("server_max_window_bits", duk_to_string(ctx, -1)) == 0)
|
|
{
|
|
if (duk_get_length(ctx, -2) > 0)
|
|
{
|
|
duk_array_shift(ctx, -2); // [headers][key][extensions][array][string][array][val1][val2]
|
|
smwb = duk_to_int(ctx, -1);
|
|
duk_pop_n(ctx, 4); // [headers][key][extensions][array]
|
|
}
|
|
else
|
|
{
|
|
duk_pop_3(ctx); // [headers][key][extensions][array]
|
|
}
|
|
}
|
|
else if (strcmp("client_max_window_bits", duk_to_string(ctx, -1)) == 0)
|
|
{
|
|
if (duk_get_length(ctx, -2) > 0)
|
|
{
|
|
duk_array_shift(ctx, -2); // [headers][key][extensions][array][string][array][val1][val2]
|
|
cmwb = duk_to_int(ctx, -1);
|
|
duk_pop_n(ctx, 4); // [headers][key][extensions][array]
|
|
}
|
|
else
|
|
{
|
|
duk_pop_3(ctx); // [headers][key][extensions][array]
|
|
}
|
|
}
|
|
else
|
|
{
|
|
duk_pop_3(ctx); // [headers][key][extensions][array]
|
|
}
|
|
}
|
|
}
|
|
|
|
decodedKey = ILibMemory_AllocateA(keyLen);
|
|
decodedKeyLen = ILibBase64Decode((unsigned char*)key, (int)keyLen, (unsigned char**)&decodedKey);
|
|
|
|
// We were upgraded to WebSocket, so we need to create a WebSocket Stream, detach the HTTPStream, and emit the event
|
|
// Upstream Readable => X => HttpStream
|
|
duk_push_this(ctx); // [HTTPStream]
|
|
if (duk_has_prop_string(ctx, -1, ILibDuktape_HTTP2CR))
|
|
{
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTP2CR); // [HTTPStream][CR]
|
|
duk_del_prop_string(ctx, -1, ILibDuktape_CR2HTTPStream);
|
|
duk_pop(ctx); // [HTTPStream]
|
|
}
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTP2PipedReadable); // [HTTPStream][readable]
|
|
if (duk_has_prop_string(ctx, -1, ILibDuktape_Socket2HttpStream))
|
|
{
|
|
duk_del_prop_string(ctx, -1, ILibDuktape_Socket2HttpStream);
|
|
}
|
|
duk_get_prop_string(ctx, -1, "unpipe"); // [HTTPStream][readable][unpipe]
|
|
duk_dup(ctx, -2); // [HTTPStream][readable][unpipe][this]
|
|
duk_call_method(ctx, 0); // [HTTPStream][readable][...]
|
|
duk_pop(ctx); // [HTTPStream][readable]
|
|
|
|
duk_get_prop_string(ctx, -1, "prependOnceListener"); // [HTTPStream][readable][prepend]
|
|
duk_dup(ctx, -2); // [HTTPStream][readable][prepend][this]
|
|
duk_push_string(ctx, "close"); // [HTTPStream][readable][prepend][this]['close']
|
|
duk_push_c_function(ctx, ILibDuktape_HttpStream_http_WebSocket_closed, DUK_VARARGS);
|
|
duk_call_method(ctx, 2); duk_pop(ctx); // [HTTPStream][readable]
|
|
|
|
duk_push_external_buffer(ctx); // [HTTPStream][readable][ext]
|
|
duk_config_buffer(ctx, -1, decodedKey, decodedKeyLen);
|
|
|
|
duk_eval_string(ctx, "require('http');"); // [HTTPStream][readable][ext][HTTP]
|
|
duk_get_prop_string(ctx, -1, "webSocketStream"); // [HTTPStream][readable][ext][HTTP][wss]
|
|
duk_remove(ctx, -2); // [HTTPStream][readable][ext][wss]
|
|
duk_push_buffer_object(ctx, -2, 0, decodedKeyLen, DUK_BUFOBJ_NODEJS_BUFFER);// [HTTPStream][readable][ext][wss][buffer]
|
|
duk_push_object(ctx); // [HTTPStream][readable][ext][wss][buffer][options]
|
|
duk_push_int(ctx, permessageDeflate); duk_put_prop_string(ctx, -2, "perMessageDeflate");
|
|
duk_push_int(ctx, smwb); duk_put_prop_string(ctx, -2, "serverMaxWindowBits");
|
|
duk_push_int(ctx, cmwb); duk_put_prop_string(ctx, -2, "clientMaxWindowBits");
|
|
duk_new(ctx, 2); // [HTTPStream][readable][ext][websocket]
|
|
duk_remove(ctx, -2); // [HTTPStream][readable][websocket]
|
|
|
|
ILibChain_Link_SetMetadata(Duktape_GetPointerProperty(ctx, -2, ILibDuktape_ChainLinkPtr), Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_OBJID, "http.webSocketStream"));
|
|
|
|
duk_get_prop_string(ctx, -3, ILibDuktape_HTTP2CR); // [HTTPStream][readable][websocket][clientRequest]
|
|
//duk_dup(ctx, -2); // [HTTPStream][readable][websocket][clientRequest][websocket]
|
|
//duk_put_prop_string(ctx, -2, ILibDuktape_CR2WS); // [HTTPStream][readable][websocket][clientRequest]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_WS2CR); // [HTTPStream][readable][websocket]
|
|
|
|
// Upstream Readable => WebSocket Encoded
|
|
duk_get_prop_string(ctx, -2, "pipe"); // [HTTPStream][readable][websocket][pipe]
|
|
duk_dup(ctx, -3); // [HTTPStream][readable][websocket][pipe][this]
|
|
duk_get_prop_string(ctx, -3, "encoded"); // [HTTPStream][readable][websocket][pipe][this][WS_ENC]
|
|
duk_call_method(ctx, 1); // [HTTPStream][readable][websocket][...]
|
|
duk_pop(ctx); // [HTTPStream][readable][websocket]
|
|
duk_remove(ctx, -2); // [HTTPStream][websocket]
|
|
|
|
if (duk_has_prop_string(ctx, -2, ILibDuktape_HTTP2PipedWritable))
|
|
{
|
|
// Web Socket Encoded => Destination Stream
|
|
duk_get_prop_string(ctx, -1, "encoded"); // [HTTPStream][websocket][WS_ENC]
|
|
duk_get_prop_string(ctx, -1, "pipe"); // [HTTPStream][websocket][WS_ENC][pipe]
|
|
duk_swap_top(ctx, -2); // [HTTPStream][websocket][pipe][this]
|
|
duk_get_prop_string(ctx, -4, ILibDuktape_HTTP2PipedWritable); // [HTTPStream][websocket][pipe][this][destination]
|
|
duk_call_method(ctx, 1); // [HTTPStream][websocket][...]
|
|
duk_pop(ctx); // [HTTPStream][websocket]
|
|
}
|
|
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WS2CR); // [HTTPStream][websocket][clientRequest]
|
|
duk_get_prop_string(ctx, -1, "emit"); // [HTTPStream][websocket][clientRequest][emit]
|
|
|
|
duk_swap_top(ctx, -2); // [HTTPStream][websocket][emit][this]
|
|
duk_push_string(ctx, "upgrade"); // [HTTPStream][websocket][emit][this][upgrade]
|
|
duk_dup(ctx, 0); // [HTTPStream][websocket][emit][this][upgrade][imsg]
|
|
duk_dup(ctx, -5); // [HTTPStream][websocket][emit][this][upgrade][imsg][websocket]
|
|
duk_get_prop_string(ctx, -1, "decoded"); // [HTTPStream][websocket][emit][this][upgrade][imsg][websocket][WS_DEC]
|
|
duk_remove(ctx, -2); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC]
|
|
|
|
// Relay 'timeout' events from socket
|
|
duk_eval_string(ctx, "require('promise').event_forwarder"); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC][FWD]
|
|
duk_get_prop_string(ctx, -3, "socket"); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC][FWD][SRCJ]
|
|
duk_push_string(ctx, "timeout"); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC][FWD][SRCJ][SRCN]
|
|
duk_dup(ctx, -4); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC][FWD][SRCJ][SRCN][TARGJ]
|
|
duk_push_string(ctx, "timeout"); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC][FWD][SRCJ][SRCN][TARGJ][TARGN]
|
|
duk_pcall(ctx, 4); duk_pop(ctx); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC]
|
|
|
|
// Relay 'setTimeout' to socket
|
|
duk_eval_string(ctx, "require('promise').event_switcher"); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC][SWITCHER]
|
|
duk_get_prop_string(ctx, -3, "socket"); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC][SWITCHER][CALLEE]
|
|
duk_get_prop_string(ctx, -1, "setTimeout"); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC][SWITCHER][CALLEE][TARGET]
|
|
duk_call(ctx, 2); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC][V]
|
|
duk_get_prop_string(ctx, -1, "func"); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC][V][FUNC]
|
|
duk_remove(ctx, -2); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC][FUNC]
|
|
duk_put_prop_string(ctx, -2, "setTimeout"); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC]
|
|
|
|
duk_push_null(ctx); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC][null]
|
|
duk_call_method(ctx, 4); duk_pop(ctx); // [HTTPStream][websocket]
|
|
|
|
if (duk_has_prop_string(ctx, -1, ILibDuktape_WS2CR))
|
|
{
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WS2CR); // [HTTPStream][websocket][request]
|
|
duk_prepare_method_call(ctx, -1, "removeAllListeners"); // [HTTPStream][websocket][request][removeAll][this]
|
|
duk_call_method(ctx, 0); duk_pop_2(ctx); // [HTTPStream][websocket]
|
|
duk_del_prop_string(ctx, -1, ILibDuktape_WS2CR);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_findAgentRequestSink(duk_context *ctx)
|
|
{
|
|
duk_push_current_function(ctx); // [func]
|
|
duk_dup(ctx, 0); // [func][elem]
|
|
duk_get_prop_string(ctx, -2, "request");// [func][elem][request]
|
|
duk_push_boolean(ctx, duk_equals(ctx, -2, -1));
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_endResponseSink(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [imsg]
|
|
//ILibDuktape_Log_Object(ctx, -1, "IMSG");
|
|
|
|
duk_del_prop_string(ctx, -1, ILibDuktape_IMSG2Ptr);
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_IMSG2HttpStream); // [imsg][httpstream]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTP2CR); // [imsg][httpstream][CR]
|
|
|
|
duk_del_prop_string(ctx, -3, ILibDuktape_IMSG2HttpStream);
|
|
duk_del_prop_string(ctx, -2, ILibDuktape_HTTP2CR);
|
|
duk_del_prop_string(ctx, -1, ILibDuktape_CR2HTTPStream);
|
|
|
|
duk_get_prop_string(ctx, -1, "unpipe"); // [imsg][httpstream][CR][unpipe]
|
|
duk_dup(ctx, -2); // [imsg][httpstream][CR][unpipe][this]
|
|
duk_call_method(ctx, 0); duk_pop(ctx); // [imsg][httpstream][CR]
|
|
|
|
duk_get_prop_string(ctx, -1, "socket"); // [imsg][httpstream][CR][socket]
|
|
duk_insert(ctx, -4); // [socket][imsg][httpstream][CR]
|
|
|
|
ILibDuktape_DeleteReadOnlyProperty(ctx, -1, "socket");
|
|
|
|
duk_prepare_method_call(ctx, -1, "removeAllListeners"); // [socket][imsg][httpstream][CR][remove][this]
|
|
duk_pcall_method(ctx, 0); duk_pop(ctx); // [socket][imsg][httpstream][CR]
|
|
|
|
if (Duktape_GetBooleanProperty(ctx, -2, "connectionCloseSpecified", 0) != 0)
|
|
{
|
|
// We cant persist this connection, so close the socket.
|
|
ILibDuktape_DeleteReadOnlyProperty(ctx, -2, ILibDuktape_HTTPStream2Socket);
|
|
ILibDuktape_DeleteReadOnlyProperty(ctx, -4, ILibDuktape_Socket2HttpStream);
|
|
duk_del_prop_string(ctx, -2, ILibDuktape_HTTP2PipedReadable);
|
|
|
|
// Agent is already listening for the 'close' event, so it'll cleanup automatically
|
|
duk_prepare_method_call(ctx, -2, "unpipe"); // [socket][imsg][httpstream][CR][unpipe][this]
|
|
duk_pcall_method(ctx, 0); duk_pop(ctx); // [socket][imsg][httpstream][CR]
|
|
duk_prepare_method_call(ctx, -4, "unpipe"); // [socket][imsg][httpstream][CR][unpipe][this]
|
|
duk_pcall_method(ctx, 0); duk_pop(ctx); // [socket][imsg][httpstream][CR]
|
|
|
|
duk_dup(ctx, -4); // [socket][imsg][httpstream][CR][socket]
|
|
duk_get_prop_string(ctx, -1, "end"); // [socket][imsg][httpstream][CR][socket][end]
|
|
duk_swap_top(ctx, -2); // [socket][imsg][httpstream][CR][end][this]
|
|
duk_call_method(ctx, 0); duk_pop(ctx); // [socket][imsg][httpstream][CR]
|
|
return(0);
|
|
}
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_CR2Agent); // [socket][imsg][httpstream][CR][Agent]
|
|
duk_get_prop_string(ctx, -1, "keepSocketAlive"); // [socket][imsg][httpstream][CR][Agent][keepSocketAlive]
|
|
duk_swap_top(ctx, -2); // [socket][imsg][httpstream][CR][keepSocketAlive][this]
|
|
duk_dup(ctx, -6); // [socket][imsg][httpstream][CR][keepSocketAlive][this][socket]
|
|
|
|
//printf("End Response -->\n");
|
|
//if (duk_has_prop_string(ctx, -1, ILibDuktape_Socket2HttpStream))
|
|
//{
|
|
// duk_get_prop_string(ctx, -1, ILibDuktape_Socket2HttpStream);
|
|
// printf(" [Socket: %p] => [HTTPStream: %p]\n", duk_get_heapptr(ctx, -2), duk_get_heapptr(ctx, -1));
|
|
// ILibDuktape_Log_Object(ctx, -1, "HTTPStream");
|
|
// duk_pop(ctx);
|
|
//}
|
|
//if (duk_has_prop_string(ctx, -1, ILibDuktape_SOCKET2OPTIONS))
|
|
//{
|
|
// duk_get_prop_string(ctx, -1, ILibDuktape_SOCKET2OPTIONS);
|
|
// ILibDuktape_Log_Object(ctx, -1, "OPTIONS");
|
|
// duk_pop(ctx);
|
|
//}
|
|
//ILibDuktape_Log_Object(ctx, -1, "SOCKET");
|
|
//printf("\n");
|
|
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // [socket][imsg][httpstream][CR]
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_responseSink(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [httpstream]
|
|
duk_dup(ctx, 0); // [httpstream][imsg]
|
|
duk_swap_top(ctx, -2); // [imsg][httpstream]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_IMSG2HttpStream); // [imsg]
|
|
ILibDuktape_EventEmitter_AddOnceEx3(ctx, 0, "end", ILibDuktape_HttpStream_http_endResponseSink);
|
|
duk_pop(ctx);
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_SocketDiedPrematurely(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [socket]
|
|
if (!duk_has_prop_string(ctx, -1, ILibDuktape_Socket2CR)) { return(0); }
|
|
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Socket2CR); // [socket][clientRequest]
|
|
ILibDuktape_Transform *tf = (ILibDuktape_Transform*)Duktape_GetPointerProperty(ctx, -1, ILibDuktape_CR2Transform);
|
|
if (tf->target->resumeImmediate != NULL)
|
|
{
|
|
duk_push_global_object(ctx); // [g]
|
|
duk_get_prop_string(ctx, -1, "clearImmediate"); // [g][clearImmediate]
|
|
duk_swap_top(ctx, -2); // [clearImmediate][this]
|
|
duk_push_heapptr(ctx, tf->target->resumeImmediate); // [clearImmediate][this][immedate]
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // ...
|
|
tf->target->resumeImmediate = NULL;
|
|
}
|
|
|
|
duk_get_prop_string(ctx, -1, "unpipe"); // [socket][clientRequest][unpipe]
|
|
duk_dup(ctx, -2); // [socket][clientRequest][unpipe][this]
|
|
duk_call_method(ctx, 0); duk_pop(ctx); // [socket][clientRequest]
|
|
|
|
ILibDuktape_ReadableStream_DestroyPausedData(tf->target);
|
|
|
|
|
|
// Need to specify some stuff, so the request body will go out again
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_CR_RequestBuffer); // [socket][clientRequest][buffer]
|
|
ILibDuktape_Http_ClientRequest_WriteData *wdata = (ILibDuktape_Http_ClientRequest_WriteData*)Duktape_GetBuffer(ctx, -1, NULL);
|
|
++wdata->retryCounter;
|
|
wdata->needRetry = 1;
|
|
wdata->bufferWriteLen = wdata->bufferLen;
|
|
wdata->headersFinished = 0;
|
|
duk_pop(ctx); // [socket][clientRequest]
|
|
|
|
if (wdata->retryCounter < 3)
|
|
{
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_CR2Agent); // [socket][clientRequest][agent]
|
|
duk_get_prop_string(ctx, -1, "requests"); // [socket][clientReqeust][agent][requests]
|
|
duk_get_prop_string(ctx, -4, ILibDuktape_Socket2AgentKey); // [socket][clientRequest][agent][requests][key]
|
|
duk_get_prop(ctx, -2); // [socket][clientRequest][agent][requests][array]
|
|
if (!duk_is_undefined(ctx, -1))
|
|
{
|
|
// We need to prepend the clientRequest into the request Queue
|
|
duk_get_prop_string(ctx, -1, "unshift"); // [socket][clientRequest][agent][requests][array][unshift]
|
|
duk_swap_top(ctx, -2); // [socket][clientRequest][agent][requests][unshift][this]
|
|
duk_dup(ctx, -5); // [socket][clientRequest][agent][requests][unshift][this][clientRequest]
|
|
duk_call_method(ctx, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ILibDuktape_EventEmitter_SetupEmit(ctx, duk_get_heapptr(ctx, -1), "error"); // [emit][this][error]
|
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "Too many failed attempts"); // [emit][this][error][err]
|
|
duk_call_method(ctx, 2);
|
|
}
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_SocketResponseReceived(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [httpStream]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2Socket); // [httpStream][socket]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Socket2CR); // [httpStream][socket][CR]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_CR2Options); // [httpStream][socket][CR][Options]
|
|
duk_del_prop_string(ctx, -1, ILibDuktape_Options2ClientRequest);
|
|
duk_pop_2(ctx); // [httpStream][socket]
|
|
duk_del_prop_string(ctx, -1, ILibDuktape_Socket2CR);
|
|
|
|
//duk_get_prop_string(ctx, -1, "removeListener"); // [httpStream][socket][removeListener]
|
|
//duk_swap_top(ctx, -2); // [httpStream][removeListener][this]
|
|
//duk_push_string(ctx, "close"); // [httpStream][removeListener][this][close]
|
|
//duk_get_prop_string(ctx, -2, ILibDuktape_Socket2DiedListener); // [httpStream][removeListener][this][close][listener]
|
|
//duk_call_method(ctx, 2);
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_OnSocketClosed(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [socket]
|
|
if (duk_has_prop_string(ctx, -1, ILibDuktape_Socket2HttpStream))
|
|
{
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Socket2HttpStream);
|
|
duk_pop(ctx);
|
|
duk_del_prop_string(ctx, -1, ILibDuktape_Socket2HttpStream);
|
|
}
|
|
duk_pop(ctx); // ...
|
|
return(0);
|
|
}
|
|
|
|
duk_ret_t ILibDuktape_HttpStream_http_OnSocketReady(duk_context *ctx)
|
|
{
|
|
void *httpStream;
|
|
|
|
duk_dup(ctx, 0); // [socket]
|
|
duk_push_string(ctx, "client"); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_FinalizerDebugMessage);
|
|
duk_push_c_function(ctx, ILibDuktape_HttpStream_http_SocketDiedPrematurely, DUK_VARARGS);
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_Socket2DiedListener); // [socket]
|
|
duk_push_this(ctx); // [socket][clientRequest]
|
|
if (Duktape_GetBooleanProperty(ctx, -1, ILibDuktape_CR_AbortCalled, 0))
|
|
{
|
|
// We were aborted, so instead of continuing, lets just put the socket back, so someone else can use it
|
|
duk_del_prop_string(ctx, -2, ILibDuktape_Socket2DiedListener);
|
|
|
|
duk_get_prop_string(ctx, -2, ILibDuktape_Socket2Agent); // [socket][clientRequest][Agent]
|
|
duk_get_prop_string(ctx, -1, "keepSocketAlive"); // [socket][clientRequest][Agent][keepAlive]
|
|
duk_swap_top(ctx, -2); // [socket][clientRequest][keepAlive][this]
|
|
duk_dup(ctx, -4); // [socket][clientRequest][keepAlive][this][socket]
|
|
duk_call_method(ctx, 1);
|
|
|
|
duk_push_this(ctx);
|
|
ILibDuktape_EventEmitter_SetupEmit(ctx, duk_get_heapptr(ctx, -1), "abort"); // [emit][this][abort]
|
|
duk_call_method(ctx, 1);
|
|
return(0);
|
|
}
|
|
|
|
ILibChain_Link_SetMetadata(Duktape_GetPointerProperty(ctx, -2, ILibDuktape_ChainLinkPtr), Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_OBJID, "http.clientRequest"));
|
|
|
|
// Register ourselves for the close event, becuase we'll need to put ourselves back in the Queue if the socket dies before we are done
|
|
duk_get_prop_string(ctx, -2, "prependOnceListener"); // [socket][clientRequest][prependOnce]
|
|
duk_dup(ctx, -3); // [socket][clientRequest][prependOnce][this]
|
|
duk_push_string(ctx, "close"); // [socket][clientRequest][prependOnce][this][close]
|
|
duk_get_prop_string(ctx, -5, ILibDuktape_Socket2DiedListener); // [socket][clientRequest][prependOnce][this][close][listener]
|
|
duk_call_method(ctx, 2); duk_pop(ctx); // [socket][clientRequest]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_Socket2CR); // [socket]
|
|
duk_push_this(ctx); // [socket][clientRequest]
|
|
if (duk_has_prop_string(ctx, -2, ILibDuktape_Socket2HttpStream))
|
|
{
|
|
// HTTP and/or TLS was already setup previously
|
|
duk_get_prop_string(ctx, -2, ILibDuktape_Socket2HttpStream); // [socket][clientRequest][HTTPStream]
|
|
ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "write", ILibDuktape_HttpStream_http_SocketResponseReceived);
|
|
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2Data); // [socket][clientRequest][HTTPStream][data]
|
|
ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)Duktape_GetBuffer(ctx, -1, NULL);
|
|
ILibWebClient_ResetWCDO(data->WCDO);
|
|
if (data->bodyStream != NULL) { ILibDuktape_readableStream_WriteEnd(data->bodyStream); data->bodyStream = NULL; }
|
|
duk_pop(ctx); // [socket][clientRequest][HTTPStream]
|
|
|
|
ILibDuktape_EventEmitter_DeleteForwardEvent(ctx, -1, "response");
|
|
ILibDuktape_EventEmitter_DeleteForwardEvent(ctx, -1, "continue");
|
|
|
|
// We need to change the events to propagate to the new clientRequest instead of the old one
|
|
duk_get_prop_string(ctx, -1, "removeAllListeners"); // [socket][clientRequest][HTTPStream][remove]
|
|
duk_dup(ctx, -2); // [socket][clientRequest][HTTPStream][remove][this]
|
|
duk_push_string(ctx, "response"); // [socket][clientRequest][HTTPStream][remove][this][response]
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // [socket][clientRequest][HTTPStream]
|
|
duk_get_prop_string(ctx, -1, "removeAllListeners"); // [socket][clientRequest][HTTPStream][remove]
|
|
duk_dup(ctx, -2); // [socket][clientRequest][HTTPStream][remove][this]
|
|
duk_push_string(ctx, "continue"); // [socket][clientRequest][HTTPStream][remove][this][continue]
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // [socket][clientRequest][HTTPStream]
|
|
duk_get_prop_string(ctx, -1, "removeAllListeners"); // [socket][clientRequest][HTTPStream][remove]
|
|
duk_dup(ctx, -2); // [socket][clientRequest][HTTPStream][remove][this]
|
|
duk_push_string(ctx, "upgrade"); // [socket][clientRequest][HTTPStream][remove][this][upgrade]
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // [socket][clientRequest][HTTPStream]
|
|
|
|
|
|
duk_push_this(ctx); // [socket][clientRequest][HTTPStream][clientRequest]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_HTTP2CR); // [socket][clientRequest][HTTPStream]
|
|
|
|
ILibDuktape_EventEmitter_ForwardEvent(ctx, -1, "response", -2, "response");
|
|
ILibDuktape_EventEmitter_ForwardEvent(ctx, -1, "continue", -2, "continue");
|
|
|
|
ILibDuktape_EventEmitter_AddOn_Infrastructure(ctx, -1, "upgrade", ILibDuktape_HttpStream_http_onUpgrade);
|
|
ILibDuktape_EventEmitter_AddOn_Infrastructure(ctx, -1, "response", ILibDuktape_HttpStream_http_responseSink);
|
|
|
|
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTP2PipedWritable); // [socket][clientRequest][HTTPStream][destination]
|
|
duk_get_prop_string(ctx, -3, ILibDuktape_CR2Options); // [socket][clientRequest][HTTPStream][destination][Options]
|
|
ILibDuktape_HttpStream_http_ConvertOptionToSend(ctx, duk_get_heapptr(ctx, -2), duk_get_heapptr(ctx, -1));
|
|
|
|
// If HTTP stream was already setup, then we need to pipe the clientRequest to the upstream object, because it wasn't attached yet.
|
|
duk_dup(ctx, -4); // [socket][clientRequest][HTTPStream][destination][Options][clientRequest]
|
|
duk_get_prop_string(ctx, -1, "pipe"); // [socket][clientRequest][HTTPStream][destination][Options][clientRequest][pipe]
|
|
duk_swap_top(ctx, -2); // [socket][clientRequest][HTTPStream][destination][Options][pipe][this]
|
|
duk_dup(ctx, -4); // [socket][clientRequest][HTTPStream][destination][Options][pipe][this][destination]
|
|
duk_push_object(ctx); // [socket][clientRequest][HTTPStream][destination][Options][pipe][this][destination][options]
|
|
duk_push_false(ctx); duk_put_prop_string(ctx, -2, "end"); // [socket][clientRequest][HTTPStream][destination][Options][pipe][this][destination][options]
|
|
|
|
duk_call_method(ctx, 2);
|
|
|
|
return(0);
|
|
}
|
|
|
|
if (duk_peval_string(ctx, "require('http').createStream(false);") != 0) // [socket][clientRequest][error]
|
|
{
|
|
// Need to Abort this connection
|
|
duk_get_prop_string(ctx, -2, "emit"); // [socket][clientRequest][error][emit]
|
|
duk_dup(ctx, -3); // [socket][clientRequest][error][emit][this]
|
|
duk_push_string(ctx, "abort"); // [socket][clientRequest][error][emit][this][ebort]
|
|
duk_push_string(ctx, duk_safe_to_string(ctx, -3)); // [socket][clientRequest][error][emit][this][abort][errorString]
|
|
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.onConnect(): "); }
|
|
return(0);
|
|
}
|
|
|
|
httpStream = duk_get_heapptr(ctx, -1); // [socket][clientRequest][httpStream]
|
|
duk_push_string(ctx, "client"); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_FinalizerDebugMessage);
|
|
duk_dup(ctx, -3); // [socket][clientRequest][httpStream][socket]
|
|
duk_dup(ctx, -2); // [socket][clientRequest][httpStream][socket][httpStream]
|
|
if (strcmp(Duktape_GetStringPropertyValue(ctx, -2, ILibDuktape_OBJID, "net.socket"), "tls.socket") == 0) { ILibDuktape_WriteID(ctx, "https.httpStream"); }
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_Socket2HttpStream); // [socket][clientRequest][httpStream][socket]
|
|
duk_pop(ctx); // [socket][clientRequest][httpStream]
|
|
duk_dup(ctx, -2); // [socket][clientRequest][httpStream][clientRequest]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_HTTP2CR); // [socket][clientRequest][httpStream]
|
|
ILibDuktape_EventEmitter_ForwardEventEx(ctx, -1, -2, "response");
|
|
ILibDuktape_EventEmitter_ForwardEventEx(ctx, -1, -2, "continue");
|
|
ILibDuktape_EventEmitter_AddOn_Infrastructure(ctx, -1, "upgrade", ILibDuktape_HttpStream_http_onUpgrade);
|
|
ILibDuktape_EventEmitter_AddOn_Infrastructure(ctx, -1, "response", ILibDuktape_HttpStream_http_responseSink);
|
|
ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "write", ILibDuktape_HttpStream_http_SocketResponseReceived);
|
|
ILibDuktape_EventEmitter_AddOnceEx3(ctx, -3, "close", ILibDuktape_HttpStream_http_OnSocketClosed); // We need to detach HttpStream when socket closes
|
|
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_CR2HTTPStream); // [socket][clientRequest]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_CR2Options); // [socket][clientRequest][options]
|
|
ILibDuktape_HttpStream_http_ConvertOptionToSend(ctx, duk_get_heapptr(ctx, -3), duk_get_heapptr(ctx, -1));
|
|
duk_pop(ctx); // [socket][clientRequest]
|
|
|
|
// ClientRequest => Socket
|
|
duk_get_prop_string(ctx, -1, "pipe"); // [socket][clientRequest][pipe]
|
|
duk_swap_top(ctx, -2); // [socket][pipe][this]
|
|
duk_dup(ctx, -3); // [socket][pipe][this][socket]
|
|
duk_push_object(ctx); // [socket][pipe][this][socket][options]
|
|
duk_push_false(ctx); duk_put_prop_string(ctx, -2, "end");
|
|
if (duk_pcall_method(ctx, 2) != 0) { return(ILibDuktape_Error(ctx, "http.onConnect(): Error Piping with socket ")); }
|
|
duk_pop(ctx); // [socket]
|
|
|
|
// Save this value, so we can unregister 'close' from socket later
|
|
duk_push_heapptr(ctx, httpStream); // [socket][httpStream]
|
|
duk_dup(ctx, -2); // [socket][httpStream][socket]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_HTTPStream2Socket); // [socket][httpStream]
|
|
duk_pop(ctx); // [socket]
|
|
// Socket => HttpStream
|
|
duk_get_prop_string(ctx, -1, "pipe"); // [socket][pipe]
|
|
duk_dup(ctx, -2); // [socket][pipe][this]
|
|
duk_push_heapptr(ctx, httpStream); // [socket][pipe][this][http]
|
|
if (duk_pcall_method(ctx, 1) != 0) { return(ILibDuktape_Error(ctx, "http.onConnect(): Error calling pipe ")); }
|
|
duk_pop(ctx); // [socket]
|
|
|
|
// HttpStream => Socket
|
|
duk_push_heapptr(ctx, httpStream); // [socket][http]
|
|
|
|
duk_get_prop_string(ctx, -1, "pipe"); // [socket][http][pipe]
|
|
duk_swap_top(ctx, -2); // [socket][pipe][this]
|
|
duk_dup(ctx, -3); // [socket][pipe][this][socket]
|
|
|
|
if (duk_pcall_method(ctx, 1) != 0) { return(ILibDuktape_Error(ctx, "http.onConnect(): Error calling pipe ")); }
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_OnConnectError(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [socket]
|
|
|
|
if (duk_has_prop_string(ctx, -1, ILibDuktape_Socket2CR))
|
|
{
|
|
// Socket was created via 'createConnection' specified by the application
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Socket2CR); // [socket][CR]
|
|
duk_get_prop_string(ctx, -1, "emit"); // [socket][CR][emit]
|
|
duk_swap_top(ctx, -2); // [socket][emit][this]
|
|
duk_push_string(ctx, "error"); // [socket][emit][this][error]
|
|
duk_dup(ctx, 0); // [socket][emit][this][error][err]
|
|
duk_call_method(ctx, 2);
|
|
}
|
|
else if (duk_has_prop_string(ctx, -1, ILibDuktape_Socket2Agent))
|
|
{
|
|
// Socket was created via 'http.Agent'
|
|
if (duk_has_prop_string(ctx, -1, "\xFF_NET_SOCKET2OPTIONS"))
|
|
{
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Socket2Agent); // [socket][agent]
|
|
duk_get_prop_string(ctx, -1, "requests"); // [socket][agent][requests]
|
|
duk_get_prop_string(ctx, -2, "getName"); // [socket][agent][requests][getName]
|
|
duk_dup(ctx, -3); // [socket][agent][requests][getName][this]
|
|
duk_get_prop_string(ctx, -5, "\xFF_NET_SOCKET2OPTIONS"); // [socket][agent][requests][getName][this][options]
|
|
duk_call_method(ctx, 1); // [socket][agent][requests][name]
|
|
duk_get_prop(ctx, -2); // [socket][agent][requests][Array]
|
|
duk_get_prop_string(ctx, -1, "pop"); // [socket][agent][requests][Array][pop]
|
|
duk_swap_top(ctx, -2); // [socket][agent][requests][pop][this]
|
|
duk_call_method(ctx, 0); // [socket][agent][requests][request]
|
|
duk_get_prop_string(ctx, -1, "emit"); // [socket][agent][requests][request][emit]
|
|
duk_swap_top(ctx, -2); // [socket][agent][requests][emit][this]
|
|
duk_push_string(ctx, "error"); // [socket][agent][requests][emit][this][error]
|
|
duk_dup(ctx, 0); // [socket][agent][requests][emit][this][error][err]
|
|
duk_call_method(ctx, 2);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
//duk_ret_t ILibDuktape_HttpStream_http_proxyData(duk_context *ctx)
|
|
//{
|
|
// char *buffer;
|
|
// duk_size_t bufferLen;
|
|
//
|
|
// buffer = (char*)Duktape_GetBuffer(ctx, 0, &bufferLen);
|
|
// if (bufferLen > 13 && ILibString_IndexOf(buffer, bufferLen, "\r\n\r\n", 4) > 0)
|
|
// {
|
|
// if (strncasecmp(buffer + 9, "200", 3) == 0)
|
|
// {
|
|
// // SUCCESS!
|
|
// duk_push_this(ctx); // [socket]
|
|
// duk_get_prop_string(ctx, -1, "removeAllListeners"); // [socket][remove]
|
|
// duk_dup(ctx, -2); // [socket][remove][this]
|
|
// duk_push_string(ctx, "data"); // [socket][remove][this]['data']
|
|
// duk_call_method(ctx, 1); duk_pop(ctx); // [socket]
|
|
//
|
|
// if (duk_has_prop_string(ctx, -1, ILibDuktape_Socket2CR))
|
|
// {
|
|
// // Socket was created with passed in createConnection
|
|
// duk_get_prop_string(ctx, -1, ILibDuktape_Socket2CR); // [socket][clientRequest]
|
|
// duk_get_prop_string(ctx, -1, "emit"); // [socket][clientRequest][emit]
|
|
// duk_swap_top(ctx, -2); // [socket][emit][this]
|
|
// duk_dup(ctx, -3); // [socket][emit][this][socket]
|
|
// if (duk_pcall_method(ctx, 1) != 0) { return(ILibDuktape_Error(ctx, "createConnection().proxyOnConnect(): ")); }
|
|
// }
|
|
// else
|
|
// {
|
|
// // Socket was created with Agent
|
|
// if (!duk_has_prop_string(ctx, -1, ILibDuktape_Socket2Agent))
|
|
// {
|
|
// return(ILibDuktape_Error(ctx, "createConnection().proxyOnConnect(): Internal Error, 'Agent' was not specified"));
|
|
// }
|
|
// else
|
|
// {
|
|
// duk_get_prop_string(ctx, -1, ILibDuktape_Socket2Agent); // [socket][agent]
|
|
// duk_get_prop_string(ctx, -1, "keepSocketAlive"); // [socket][agent][keepSocketAlive]
|
|
// duk_swap_top(ctx, -2); // [socket][keepSocketAlive][this]
|
|
// duk_dup(ctx, -3); // [socket][keepSocketAlive][this][socket]
|
|
// if (duk_pcall_method(ctx, 1) != 0) { return(ILibDuktape_Error(ctx, "createConnection().proxyOnConnect(): Error calling Agent.keepSocketAlive [%s]", duk_safe_to_string(ctx, -1))); }
|
|
// }
|
|
// }
|
|
// return(0);
|
|
// }
|
|
// else
|
|
// {
|
|
// // FAIL!
|
|
// }
|
|
// }
|
|
// else
|
|
// {
|
|
// // We don't have the entire response yet
|
|
// duk_push_this(ctx); // [socket]
|
|
// duk_get_prop_string(ctx, -1, "unshift"); // [socket][unshift]
|
|
// duk_swap_top(ctx, -2); // [unshift][this]
|
|
// duk_dup(ctx, 0); // [unshift][this][chunk]
|
|
// duk_call_method(ctx, 1);
|
|
// }
|
|
// return(0);
|
|
//}
|
|
duk_ret_t ILibDuktape_Agent_findConnection(duk_context *ctx)
|
|
{
|
|
duk_push_current_function(ctx); // [func]
|
|
duk_get_prop_string(ctx, -1, "socket"); // [func][socket]
|
|
duk_dup(ctx, 0); // [func][socket][socket]
|
|
if (duk_equals(ctx, -2, -1))
|
|
{
|
|
duk_push_true(ctx);
|
|
}
|
|
else
|
|
{
|
|
duk_push_false(ctx);
|
|
}
|
|
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_Agent_connectionEnded(duk_context *ctx)
|
|
{
|
|
duk_idx_t top;
|
|
duk_push_this(ctx); // [socket]
|
|
duk_prepare_method_call(ctx, -1, "removeAllListeners"); // [socket][remove][this]
|
|
duk_call_method(ctx, 0); duk_pop(ctx); // [socket]
|
|
|
|
char *key = (char*)Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_Socket2AgentKey, NULL);
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Socket2Agent); // [socket][agent]
|
|
top = duk_get_top(ctx);
|
|
duk_get_prop_string(ctx, -1, "sockets"); // [socket][agent][table]
|
|
duk_get_prop_string(ctx, -1, key); // [socket][agent][table][array]
|
|
if (duk_is_array(ctx, -1))
|
|
{
|
|
duk_push_c_function(ctx, ILibDuktape_Agent_findConnection, DUK_VARARGS); //array][func]
|
|
duk_prepare_method_call(ctx, -2, "findIndex"); // [socket][agent][table][array][func][findIndex][this]
|
|
duk_dup(ctx, -3); // [socket][agent][table][array][func][findIndex][this][func]
|
|
duk_push_this(ctx); // [socket][agent][table][array][func][findIndex][this][func][socket]
|
|
duk_put_prop_string(ctx, -2, "socket"); // [socket][agent][table][array][func][findIndex][this][func]
|
|
duk_call_method(ctx, 1); // [socket][agent][table][array][func][int]
|
|
duk_del_prop_string(ctx, -2, "socket");
|
|
if (duk_get_int(ctx, -1) >= 0)
|
|
{
|
|
duk_prepare_method_call(ctx, -3, "splice"); // [socket][agent][table][array][func][int][splice][this]
|
|
duk_dup(ctx, -3); // [socket][agent][table][array][func][int][splice][this][index]
|
|
duk_push_int(ctx, 1); // [socket][agent][table][array][func][int][splice][this][index][i]
|
|
duk_call_method(ctx, 2); duk_pop(ctx); // [socket][agent][table][array][func][int]
|
|
}
|
|
}
|
|
|
|
duk_set_top(ctx, top); // [socket][agent]
|
|
duk_get_prop_string(ctx, -1, "freeSockets"); // [socket][agent][table]
|
|
ILibDuktape_RemoveObjFromTable(ctx, -1, key, duk_get_heapptr(ctx, -3));
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_OnConnect(duk_context *ctx)
|
|
{
|
|
duk_ret_t retVal = 0;
|
|
duk_push_this(ctx); // [socket]
|
|
if (duk_has_prop_string(ctx, -1, ILibDuktape_Socket2CR))
|
|
{
|
|
// Socket was created with passed in createConnection
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Socket2CR); // [socket][clientRequest]
|
|
duk_get_prop_string(ctx, -1, "emit"); // [socket][clientRequest][emit]
|
|
duk_swap_top(ctx, -2); // [socket][emit][this]
|
|
duk_dup(ctx, -3); // [socket][emit][this][socket]
|
|
if (duk_pcall_method(ctx, 1) != 0) { retVal = ILibDuktape_Error(ctx, "createConnection().onConnect(): "); }
|
|
}
|
|
else
|
|
{
|
|
// Socket was created with Agent
|
|
if (!duk_has_prop_string(ctx, -1, ILibDuktape_Socket2Agent))
|
|
{
|
|
retVal = ILibDuktape_Error(ctx, "createConnection().onConnect(): Internal Error, 'Agent' was not specified");
|
|
}
|
|
else
|
|
{
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Socket2Agent); // [socket][agent]
|
|
ILibDuktape_EventEmitter_AddOn_Infrastructure(ctx, -2, "end", ILibDuktape_Agent_connectionEnded);
|
|
|
|
duk_get_prop_string(ctx, -1, "keepSocketAlive"); // [socket][agent][keepSocketAlive]
|
|
duk_swap_top(ctx, -2); // [socket][keepSocketAlive][this]
|
|
duk_dup(ctx, -3); // [socket][keepSocketAlive][this][socket]
|
|
if (duk_pcall_method(ctx, 1) != 0) { retVal = ILibDuktape_Error(ctx, "createConnection().onConnect(): Error calling Agent.keepSocketAlive [%s]", duk_safe_to_string(ctx, -1)); }
|
|
}
|
|
}
|
|
return(retVal);
|
|
}
|
|
|
|
void ILibDuktape_HttpStream_http_request_transformPiped(struct ILibDuktape_Transform *sender, void *user)
|
|
{
|
|
char tmp[100];
|
|
int tmpLen;
|
|
|
|
ILibDuktape_Http_ClientRequest_WriteData *data = (ILibDuktape_Http_ClientRequest_WriteData*)user;
|
|
if(data->needRetry != 0)
|
|
{
|
|
data->headersFinished = 1;
|
|
if (sender->writerEnded != 0)
|
|
{
|
|
tmpLen = sprintf_s(tmp, sizeof(tmp), "Content-Length: %d\r\n\r\n", (int)data->bufferWriteLen);
|
|
}
|
|
else
|
|
{
|
|
tmpLen = sprintf_s(tmp, sizeof(tmp), "Transfer-Encoding: chunked\r\n\r\n%X\r\n", (unsigned int)data->bufferWriteLen);
|
|
}
|
|
ILibDuktape_readableStream_WriteData(sender->target, tmp, tmpLen);
|
|
if (data->bufferWriteLen > 0)
|
|
{
|
|
ILibDuktape_readableStream_WriteData(sender->target, data->buffer, (int)data->bufferWriteLen);
|
|
if (sender->writerEnded == 0) { ILibDuktape_readableStream_WriteData(sender->target, "\r\n", 2); }
|
|
|
|
free(data->buffer);
|
|
data->buffer = NULL;
|
|
}
|
|
|
|
data->bufferLen = data->bufferWriteLen = 0;
|
|
data->needRetry = 0;
|
|
}
|
|
}
|
|
void ILibDuktape_HttpStream_http_request_transform(struct ILibDuktape_Transform *sender, int Reserved, int flush, char *buffer, int bufferLen, void *user)
|
|
{
|
|
ILibDuktape_Http_ClientRequest_WriteData *data = (ILibDuktape_Http_ClientRequest_WriteData*)user;
|
|
|
|
char tmp[100];
|
|
int tmpLen;
|
|
|
|
if (data->headersFinished == 0)
|
|
{
|
|
// Need to write out the end of the headers
|
|
data->headersFinished = 1;
|
|
if (bufferLen > 0)
|
|
{
|
|
ILibMemory_AllocateRaw(data->buffer, bufferLen);
|
|
data->bufferLen = bufferLen;
|
|
memcpy_s(data->buffer, bufferLen, buffer, bufferLen);
|
|
}
|
|
|
|
if (flush != 0)
|
|
{
|
|
tmpLen = sprintf_s(tmp, sizeof(tmp), "Content-Length: %d\r\n\r\n", bufferLen);
|
|
ILibDuktape_readableStream_WriteData(sender->target, tmp, tmpLen);
|
|
if (bufferLen > 0)
|
|
{
|
|
ILibDuktape_readableStream_WriteData(sender->target, buffer, bufferLen);
|
|
}
|
|
data->contentLengthSpecified = 1;
|
|
}
|
|
else
|
|
{
|
|
tmpLen = sprintf_s(tmp, sizeof(tmp), "Transfer-Encoding: chunked\r\n\r\n%X\r\n", bufferLen);
|
|
ILibDuktape_readableStream_WriteData(sender->target, tmp, tmpLen);
|
|
if (bufferLen > 0)
|
|
{
|
|
ILibDuktape_readableStream_WriteData(sender->target, buffer, bufferLen);
|
|
}
|
|
ILibDuktape_readableStream_WriteData(sender->target, "\r\n", 2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tmpLen = sprintf_s(tmp, sizeof(tmp), "%X\r\n", bufferLen);
|
|
ILibDuktape_readableStream_WriteData(sender->target, tmp, tmpLen);
|
|
if (bufferLen > 0)
|
|
{
|
|
ILibDuktape_readableStream_WriteData(sender->target, buffer, bufferLen);
|
|
ILibMemory_ReallocateRaw(&(data->buffer), data->bufferLen + bufferLen);
|
|
memcpy_s(data->buffer + data->bufferLen, bufferLen, buffer, bufferLen);
|
|
data->bufferLen = data->bufferLen + bufferLen;
|
|
}
|
|
ILibDuktape_readableStream_WriteData(sender->target, "\r\n", 2);
|
|
}
|
|
|
|
}
|
|
duk_ret_t ILibDuktape_ClientRequest_Finalizer(duk_context *ctx)
|
|
{
|
|
if (duk_has_prop_string(ctx, 0, ILibDuktape_CR_RequestBuffer))
|
|
{
|
|
duk_get_prop_string(ctx, 0, ILibDuktape_CR_RequestBuffer);
|
|
ILibDuktape_Http_ClientRequest_WriteData *data = (ILibDuktape_Http_ClientRequest_WriteData*)Duktape_GetBuffer(ctx, -1, NULL);
|
|
if (data->buffer != NULL)
|
|
{
|
|
free(data->buffer);
|
|
data->buffer = NULL;
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
duk_ret_t ILibDuktape_HttpStream_http_request_abort_closed(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [socket]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Socket2CR_Abort); // [socket][ClientRequest]
|
|
ILibDuktape_EventEmitter_SetupEmit(ctx, duk_get_heapptr(ctx, -1), "abort"); // [socket][ClientRequest][emit][this][abort]
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // [socket][ClientRequest]
|
|
duk_del_prop_string(ctx, -2, ILibDuktape_Socket2CR_Abort);
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_request_abort(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [ClientRequest]
|
|
duk_push_true(ctx); // [ClientRequest][true]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_CR_AbortCalled); // [ClientRequest]
|
|
|
|
if (duk_has_prop_string(ctx, -1, ILibDuktape_CR2HTTPStream))
|
|
{
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_CR2HTTPStream); // [ClientRequest][HTTPStream]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2Socket);// [ClientRequest][HTTPStream][socket]
|
|
|
|
// Need to hookup close event, so we can propagate 'abort'
|
|
ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "close", ILibDuktape_HttpStream_http_request_abort_closed);
|
|
duk_dup(ctx, -3); duk_put_prop_string(ctx, -2, ILibDuktape_Socket2CR_Abort);
|
|
|
|
duk_get_prop_string(ctx, -1, "end"); // [ClientRequest][HTTPStream][socket][end]
|
|
duk_swap_top(ctx, -2); // [ClientRequest][HTTPStream][end][this]
|
|
duk_call_method(ctx, 0); // [ClientRequest][HTTPStream][...]
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
duk_ret_t ILibDuktape_HttpStream_http_request(duk_context *ctx)
|
|
{
|
|
char *proto;
|
|
duk_size_t protoLen;
|
|
int isTLS = 0;
|
|
int nargs = duk_get_top(ctx);
|
|
|
|
duk_require_stack(ctx, DUK_API_ENTRY_STACK);
|
|
|
|
if (duk_is_string(ctx, 0))
|
|
{
|
|
// Call 'get' instead, since we already handle that case there...
|
|
duk_push_this(ctx); // [http]
|
|
duk_get_prop_string(ctx, -1, "get"); // [http][get]
|
|
duk_swap_top(ctx, -2); // [get][this]
|
|
duk_dup(ctx, 0); // [get][this][uri]
|
|
if (nargs > 1 && duk_is_function(ctx, 1))
|
|
{
|
|
duk_dup(ctx, 1); // [get][this][uri][callback]
|
|
duk_call_method(ctx, 2); // [retVal]
|
|
}
|
|
else
|
|
{
|
|
duk_call_method(ctx, 1); // [retVal]
|
|
}
|
|
return(1);
|
|
}
|
|
else
|
|
{
|
|
// Make sure 'host' field is present
|
|
duk_dup(ctx, 0); // [options]
|
|
duk_get_prop_string(ctx, -1, "protocol"); // [options][protocol]
|
|
proto = (char*)Duktape_GetBuffer(ctx, -1, &protoLen);
|
|
if ((protoLen == 4 && strncasecmp(proto, "wss:", 4) == 0) || (protoLen == 3 && strncasecmp(proto, "ws:", 3) == 0))
|
|
{
|
|
duk_dup(ctx, 0); // [options][protocol][options]
|
|
duk_push_false(ctx); // [options][protocol][options][false]
|
|
duk_put_prop_string(ctx, -2, "agent"); // [options][protocol][options]
|
|
duk_pop(ctx); // [options][protocol]
|
|
}
|
|
if ((protoLen == 4 && strncasecmp(proto, "wss:", 4) == 0) || (protoLen == 6 && strncasecmp(proto, "https:", 6) == 0)) { isTLS = 1; }
|
|
|
|
duk_pop(ctx); // [options]
|
|
if (!duk_has_prop_string(ctx, -1, "headers"))
|
|
{
|
|
duk_push_object(ctx); // [options][headers]
|
|
duk_get_prop_string(ctx, -2, "host"); // [options][headers][hostname]
|
|
duk_get_prop_string(ctx, -1, "concat"); // [options][headers][hostname][concat]
|
|
duk_swap_top(ctx, -2); // [options][headers][concat][this]
|
|
duk_push_string(ctx, ":"); // [options][headers][concat][this][:]
|
|
duk_get_prop_string(ctx, -5, "port"); // [options][headers][concat][this][:][port]
|
|
if ((strcmp("443", (char*)duk_to_string(ctx, -1)) == 0 && isTLS == 1) || (strcmp("80", (char*)duk_to_string(ctx, -1)) == 0 && isTLS == 0))
|
|
{
|
|
// No need to add port to host [options][headers][concat][this][:][port]
|
|
duk_pop_2(ctx); // [options][headers][concat][this]
|
|
duk_remove(ctx, -2); // [options][headers][host]
|
|
}
|
|
else
|
|
{
|
|
// Add port to host
|
|
duk_call_method(ctx, 2); // [options][headers][hostname]
|
|
}
|
|
duk_put_prop_string(ctx, -2, "Host"); // [options][headers]
|
|
duk_put_prop_string(ctx, -2, "headers"); // [options]
|
|
}
|
|
duk_get_prop_string(ctx, -1, "headers"); // [options][headers]
|
|
if (duk_has_prop_string(ctx, -1, "Expect") && !duk_has_prop_string(ctx, -1, "Transfer-Encoding") && !duk_has_prop_string(ctx, -1, "Content-Length"))
|
|
{
|
|
return(ILibDuktape_Error(ctx, "http.request(): Cannot specify header 'Expect' without specifying 'Content-Length' or 'Transfer-Encoding'"));
|
|
}
|
|
duk_pop_2(ctx); // ...
|
|
}
|
|
|
|
duk_dup(ctx, 0); // [options]
|
|
duk_push_object(ctx); // [options][clientRequest]
|
|
|
|
duk_dup(ctx, -1); // [options][clientRequest][dup]
|
|
duk_put_prop_string(ctx, -3, ILibDuktape_Options2ClientRequest);// [options][clientRequest]
|
|
duk_remove(ctx, -2); // [clientRequest]
|
|
duk_push_this(ctx); // [clientRequest][http]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_CR2HTTP); // [clientRequest]
|
|
|
|
duk_push_false(ctx);
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_CR_EndCalled); // [clientRequest]
|
|
duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_Http_ClientRequest_WriteData)); // [clientRequest][buffer]
|
|
ILibDuktape_Http_ClientRequest_WriteData *wdata = (ILibDuktape_Http_ClientRequest_WriteData*)Duktape_GetBuffer(ctx, -1, NULL);
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_CR_RequestBuffer); // [clientRequest]
|
|
memset(wdata, 0, sizeof(ILibDuktape_Http_ClientRequest_WriteData));
|
|
|
|
duk_push_pointer(ctx, ILibDuktape_Transform_Init(ctx, ILibDuktape_HttpStream_http_request_transform, ILibDuktape_HttpStream_http_request_transformPiped, wdata));
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_CR2Transform); // [clientRequest]
|
|
|
|
ILibDuktape_WriteID(ctx, isTLS ? "https.clientRequest" : "http.clientRequest");
|
|
ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_Create(ctx);
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "abort");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "connect");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "continue");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "response");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "socket");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "timeout");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "upgrade");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "error");
|
|
|
|
ILibDuktape_EventEmitter_AddOn_Infrastructure(ctx, -1, "socket", ILibDuktape_HttpStream_http_OnSocketReady);
|
|
ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "~", ILibDuktape_ClientRequest_Finalizer);
|
|
ILibDuktape_CreateProperty_InstanceMethod(ctx, "abort", ILibDuktape_HttpStream_http_request_abort, 0);
|
|
|
|
if (nargs > 1 && duk_is_function(ctx, 1))
|
|
{
|
|
duk_get_prop_string(ctx, -1, "once"); // [clientRequest][once]
|
|
duk_dup(ctx, -2); // [clientRequest][once][this]
|
|
duk_push_string(ctx, "response"); // [clientRequest][once][this][response]
|
|
duk_dup(ctx, 1); // [clientRequest][once][this][response][handler]
|
|
duk_call_method(ctx, 2); duk_pop(ctx); // [clientRequest]
|
|
}
|
|
|
|
duk_dup(ctx, 0); // [clientRequest][options]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_CR2Options); // [clientReqeust]
|
|
|
|
void *createConnection = NULL;
|
|
void *agent = NULL;
|
|
|
|
if (duk_has_prop_string(ctx, 0, "createConnection"))
|
|
{
|
|
createConnection = Duktape_GetHeapptrProperty(ctx, 0, "createConnection");
|
|
}
|
|
else
|
|
{
|
|
if (duk_has_prop_string(ctx, 0, "agent"))
|
|
{
|
|
duk_get_prop_string(ctx, 0, "agent"); // [clientRequest][Agent]
|
|
if (duk_is_boolean(ctx, -1))
|
|
{
|
|
if (duk_get_boolean(ctx, -1) == 0)
|
|
{
|
|
duk_pop(ctx); // [clientRequest]
|
|
if (isTLS == 0)
|
|
{
|
|
duk_eval_string(ctx, "require('http').Agent();"); // [clientRequest][tempAgent]
|
|
}
|
|
else
|
|
{
|
|
duk_eval_string(ctx, "require('https').Agent();"); // [clientRequest][tempAgent]
|
|
}
|
|
agent = duk_get_heapptr(ctx, -1);
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_CR2Agent); // [clientRequest]
|
|
}
|
|
else
|
|
{
|
|
duk_pop(ctx); // [clientRequest]
|
|
|
|
if (isTLS == 0)
|
|
{
|
|
duk_eval_string(ctx, "require('http')"); // [clientRequest][http]
|
|
}
|
|
else
|
|
{
|
|
duk_eval_string(ctx, "require('https')"); // [clientRequest][http]
|
|
}
|
|
duk_get_prop_string(ctx, -1, "globalAgent"); // [clientRequest][http][agent]
|
|
agent = duk_get_heapptr(ctx, -1);
|
|
duk_remove(ctx, -2); // [clientRequest][agent]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_CR2Agent); // [clientRequest]
|
|
}
|
|
}
|
|
else if (duk_is_object(ctx, -1))
|
|
{
|
|
agent = duk_get_heapptr(ctx, -1); // [clientRequest][agent]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_CR2Agent); // [clientRequest]
|
|
}
|
|
else
|
|
{
|
|
return(ILibDuktape_Error(ctx, "http.request(): Invalid Option Parameter 'Agent'"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
duk_push_this(ctx); // [clientRequest][http]
|
|
duk_get_prop_string(ctx, -1, "globalAgent"); // [clientRequest][http][agent]
|
|
agent = duk_get_heapptr(ctx, -1);
|
|
duk_remove(ctx, -2); // [clientRequest][agent]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_CR2Agent); // [clientRequest]
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Proxy
|
|
//
|
|
ILibDuktape_globalTunnel_data *globalTunnel = ILibDuktape_GetGlobalTunnel(ctx);
|
|
if (duk_has_prop_string(ctx, 0, "proxy"))
|
|
{
|
|
duk_get_prop_string(ctx, 0, "proxy"); // [clientRequest][proxy]
|
|
if (duk_is_string(ctx, -1)) { if (strcmp((char*)duk_get_string(ctx, -1), "none") == 0) { globalTunnel = NULL; duk_del_prop_string(ctx, -2, "proxy"); } }
|
|
duk_pop(ctx); // [clientRequest]
|
|
}
|
|
if (globalTunnel != NULL && !duk_has_prop_string(ctx, 0, "proxy"))
|
|
{
|
|
duk_dup(ctx, 0); // [options]
|
|
duk_push_object(ctx); // [options][proxy]
|
|
|
|
duk_push_string(ctx, ILibRemoteLogging_ConvertAddress((struct sockaddr*)&(globalTunnel->proxyServer)));
|
|
duk_put_prop_string(ctx, -2, "host");
|
|
duk_push_int(ctx, (int)ntohs(globalTunnel->proxyServer.sin6_port));
|
|
duk_put_prop_string(ctx, -2, "port"); // [options][proxy]
|
|
if (globalTunnel->proxyUser[0] != 0 && globalTunnel->proxyPass[0] != 0)
|
|
{
|
|
duk_push_string(ctx, globalTunnel->proxyUser); duk_put_prop_string(ctx, -2, "username");
|
|
duk_push_string(ctx, globalTunnel->proxyPass); duk_put_prop_string(ctx, -2, "password");
|
|
}
|
|
|
|
duk_put_prop_string(ctx, -2, "proxy"); // [options]
|
|
|
|
duk_pop(ctx); // ...
|
|
}
|
|
|
|
if (createConnection != NULL) // [clientRequest]
|
|
{
|
|
duk_push_heapptr(ctx, createConnection); // [clientRequest][createConnection]
|
|
duk_dup(ctx, 0); // [clientRequest][createConnection][options]
|
|
duk_call(ctx, 1); // [clientRequest][socket]
|
|
duk_dup(ctx, -1); // [clientRequest][socket][clientRequest]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_Socket2CR); // [clientRequest][socket]
|
|
ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "connect", ILibDuktape_HttpStream_http_OnConnect);
|
|
ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "error", ILibDuktape_HttpStream_http_OnConnectError);
|
|
ILibDuktape_CreateReadonlyProperty(ctx, "socket"); // [clientRequest]
|
|
}
|
|
else if (agent != NULL) // [clientRequest]
|
|
{
|
|
duk_push_heapptr(ctx, agent); // [clientRequest][agent]
|
|
duk_get_prop_string(ctx, -1, "getName"); // [clientRequest][agent][getName]
|
|
duk_dup(ctx, -2); // [clientRequest][agent][getName][this]
|
|
duk_dup(ctx, 0); // [clientRequest][agent][getName][this][options]
|
|
duk_call_method(ctx, 1); // [clientRequest][agent][key]
|
|
duk_get_prop_string(ctx, -2, "freeSockets"); // [clientRequest][agent][key][freeSockets]
|
|
duk_dup(ctx, -2); // [clientRequest][agent][key][freeSockets][key]
|
|
duk_get_prop(ctx, -2); // [clientRequest][agent][key][freeSockets][Array]
|
|
if (!duk_is_undefined(ctx, -1))
|
|
{
|
|
duk_get_prop_string(ctx, -1, "shift"); // [clientRequest][agent][key][freeSockets][Array][shift]
|
|
duk_swap_top(ctx, -2); // [clientRequest][agent][key][freeSockets][shift][this]
|
|
duk_call_method(ctx, 0); // [clientRequest][agent][key][freeSockets][socket]
|
|
if (!duk_is_undefined(ctx, -1))
|
|
{
|
|
duk_remove(ctx, -2); // [clientRequest][agent][key][socket]
|
|
duk_get_prop_string(ctx, -3, "reuseSocket"); // [clientRequest][agent][key][socket][reuseSocket]
|
|
duk_dup(ctx, -4); // [clientRequest][agent][key][socket][reuseSocket][this]
|
|
duk_dup(ctx, -3); // [clientRequest][agent][key][socket][reuseSocket][this][socket]
|
|
duk_dup(ctx, -7); // [clientRequest][agent][key][socket][reuseSocket][this][socket][request]
|
|
duk_call_method(ctx, 2); // [clientRequest][agent][key][socket][undefined]
|
|
duk_pop_n(ctx, 4); // [clientRequest]
|
|
agent = NULL;
|
|
}
|
|
else
|
|
{
|
|
duk_pop_2(ctx); // [clientRequest][agent][key]
|
|
}
|
|
}
|
|
else
|
|
{
|
|
duk_pop_2(ctx); // [clientRequest][agent][key]
|
|
}
|
|
|
|
if (agent != NULL) // [clientRequest][agent][key]
|
|
{
|
|
// If we are here, it means there was not a freeSocket
|
|
|
|
// Let's start by adding ourselves to the Pending Requests Queue
|
|
duk_get_prop_string(ctx, -2, "requests"); // [clientRequest][agent][key][requests]
|
|
duk_dup(ctx, -2); // [clientRequest][agent][key][requests][key]
|
|
if (!duk_has_prop(ctx, -2)) // [clientRequest][agent][key][requests]
|
|
{
|
|
// No waiting requests, so attach a new queue
|
|
duk_dup(ctx, -2); // [clientRequest][agent][key][requests][key]
|
|
duk_push_array(ctx); // [clientRequest][agent][key][requests][key][value]
|
|
duk_get_prop_string(ctx, -1, "push"); // [clientRequest][agent][key][requests][key][value][push]
|
|
duk_dup(ctx, -2); // [clientRequest][agent][key][requests][key][value][push][this]
|
|
duk_dup(ctx, -8); // [clientRequest][agent][key][requests][key][value][push][this][clieentRequest]
|
|
duk_call_method(ctx, 1); // [clientRequest][agent][key][requests][key][value][retVal]
|
|
duk_pop(ctx); // [clientRequest][agent][key][requests][key][value]
|
|
duk_put_prop(ctx, -3); // [clientRequest][agent][key][requests]
|
|
duk_pop(ctx); // [clientRequest][agent][key]
|
|
}
|
|
else
|
|
{
|
|
// There is already a queue here
|
|
duk_dup(ctx, -2); // [clientRequest][agent][key][requests][key]
|
|
duk_get_prop(ctx, -2); // [clientRequest][agent][key][requests][array]
|
|
duk_get_prop_string(ctx, -1, "push"); // [clientRequest][agent][key][requests][array][push]
|
|
duk_swap_top(ctx, -2); // [clientRequest][agent][key][requests][push][this]
|
|
duk_dup(ctx, -6); // [clientRequest][agent][key][requests][push][this][clientRequest]
|
|
duk_call_method(ctx, 1); // [clientRequest][agent][key][requests][retVal]
|
|
duk_pop_2(ctx); // [clientRequest][agent][key]
|
|
}
|
|
|
|
// Let's check to see if there is already a socket in use talking to our same host
|
|
duk_get_prop_string(ctx, -2, "sockets"); // [clientRequest][agent][key][sockets]
|
|
duk_dup(ctx, -2); // [clientRequest][agent][key][sockets][key]
|
|
duk_get_prop(ctx, -2); // [clientRequest][agent][key][sockets][Array]
|
|
if (duk_is_undefined(ctx, -1) || duk_get_length(ctx, -1) < (duk_size_t)Duktape_GetIntPropertyValue(ctx, -4, "maxSockets", 0))
|
|
{
|
|
// We can create a new socket
|
|
duk_pop_3(ctx); // [clientRequest][agent]
|
|
duk_dup(ctx, -1); // [clientRequest][agent][agent]
|
|
duk_get_prop_string(ctx, -1, "createConnection"); // [clientRequest][agent][agent][createConnection]
|
|
duk_swap_top(ctx, -2); // [clientRequest][agent][createConnection][this]
|
|
duk_dup(ctx, 0); // [clientRequest][agent][createConnection][this][options]
|
|
if (duk_has_prop_string(ctx, -1, "checkClientIdentity"))
|
|
{
|
|
duk_push_c_function(ctx, ILibDuktape_HttpStream_http_checkIdentity, DUK_VARARGS); // [clientRequest][agent][createConnection][this][options][checkIdentity]
|
|
duk_get_prop_string(ctx, -2, "checkClientIdentity"); // [clientRequest][agent][createConnection][this][options][checkIdentity][checkClient]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_FUNC); // [clientRequest][agent][createConnection][this][options][checkIdentity]
|
|
duk_dup(ctx, -6); // [clientRequest][agent][createConnection][this][options][checkIdentity][ClientRequest]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_ClientRequest); // [clientRequest][agent][createConnection][this][options][checkIdentity]
|
|
duk_put_prop_string(ctx, -2, "checkClientIdentity"); // [clientRequest][agent][createConnection][this][options]
|
|
}
|
|
|
|
duk_push_c_function(ctx, ILibDuktape_HttpStream_http_OnConnect, DUK_VARARGS);
|
|
duk_call_method(ctx, 2); // [clientRequest][agent][socket]
|
|
duk_swap_top(ctx, -2); // [clientRequest][socket][agent]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_Socket2Agent); // [clientRequest][socket]
|
|
ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "error", ILibDuktape_HttpStream_http_OnConnectError);
|
|
duk_pop(ctx); // [clientRequest]
|
|
}
|
|
else
|
|
{
|
|
duk_pop_n(ctx, 4); // [clientRequest]
|
|
}
|
|
}
|
|
}
|
|
|
|
return(1);
|
|
}
|
|
|
|
duk_ret_t ILibDuktape_HttpStream_http_server_close(duk_context *ctx)
|
|
{
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_server_upgradeWebsocket(duk_context *ctx)
|
|
{
|
|
char wsguid[] = WEBSOCKET_GUID;
|
|
char *key, *keyResult;
|
|
int keyResultLen;
|
|
duk_size_t keyLen;
|
|
SHA_CTX c;
|
|
char shavalue[21];
|
|
|
|
duk_push_this(ctx); // [socket]
|
|
duk_get_prop_string(ctx, -1, "Digest_writeUnauthorized"); // [socket][func]
|
|
duk_del_prop_string(ctx, -1, "imsg");
|
|
duk_pop(ctx); // [socket]
|
|
|
|
duk_push_current_function(ctx); // [socket][func]
|
|
duk_get_prop_string(ctx, -2, "unpipe"); // [socket][func][unpipe]
|
|
duk_dup(ctx, -3); // [socket][func][unpipe][this]
|
|
duk_call_method(ctx, 0); duk_pop(ctx); // [socket][func]
|
|
|
|
duk_get_prop_string(ctx, -1, "imsg"); // [socket][func][imsg]
|
|
duk_get_prop_string(ctx, -1, "headers"); // [socket][func][imsg][headers]
|
|
duk_get_prop_string(ctx, -1, "Sec-WebSocket-Key"); // [socket][func][imsg][headers][key]
|
|
duk_del_prop_string(ctx, -4, "imsg");
|
|
|
|
key = (char*)Duktape_GetBuffer(ctx, -1, &keyLen);
|
|
keyResult = ILibString_Cat(key, (int)keyLen, wsguid, sizeof(wsguid));
|
|
|
|
SHA1_Init(&c);
|
|
SHA1_Update(&c, keyResult, strnlen_s(keyResult, sizeof(wsguid) + keyLen));
|
|
SHA1_Final((unsigned char*)shavalue, &c);
|
|
shavalue[20] = 0;
|
|
free(keyResult);
|
|
keyResult = NULL;
|
|
|
|
keyResultLen = ILibBase64Encode((unsigned char*)shavalue, 20, (unsigned char**)&keyResult);
|
|
|
|
duk_push_this(ctx); // [socket]
|
|
duk_get_prop_string(ctx, -1, "write"); // [socket][write]
|
|
duk_dup(ctx, -2); // [socket][write][this]
|
|
duk_push_string(ctx, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ");
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // ...
|
|
|
|
duk_push_this(ctx); // [socket]
|
|
duk_get_prop_string(ctx, -1, "write"); // [socket][write]
|
|
duk_dup(ctx, -2); // [socket][write][this]
|
|
duk_push_lstring(ctx, keyResult, keyResultLen);
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // ...
|
|
|
|
duk_push_this(ctx); // [socket]
|
|
duk_get_prop_string(ctx, -1, "write"); // [socket][write]
|
|
duk_dup(ctx, -2); // [socket][write][this]
|
|
duk_push_string(ctx, "\r\n\r\n");
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // ...
|
|
|
|
duk_eval_string(ctx, "require('http');"); // [http]
|
|
duk_get_prop_string(ctx, -1, "webSocketStream"); // [http][constructor]
|
|
duk_push_lstring(ctx, keyResult, keyResultLen); // [http][constructor][key]
|
|
duk_new(ctx, 1); // [http][wss]
|
|
((ILibDuktape_WebSocket_State*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_WebSocket_StatePtr))->noMasking = 1; // Server cannot mask WebSockets when sending data
|
|
|
|
duk_push_this(ctx); // [http][wss][socket]
|
|
duk_get_prop_string(ctx, -1, "pipe"); // [http][wss][socket][pipe]
|
|
duk_swap_top(ctx, -2); // [http][wss][pipe][this]
|
|
duk_get_prop_string(ctx, -3, "encoded"); // [http][wss][pipe][this][WS_ENC]
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // [http][wss]
|
|
|
|
duk_get_prop_string(ctx, -1, "encoded"); // [http][wss][WS_ENC]
|
|
duk_get_prop_string(ctx, -1, "pipe"); // [http][wss][WS_ENC][pipe]
|
|
duk_swap_top(ctx, -2); // [http][wss][pipe][this]
|
|
duk_push_this(ctx); // [http][wss][pipe][this][socket]
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // [http][wss]
|
|
|
|
duk_get_prop_string(ctx, -1, "decoded"); // [http][wss][WS_DEC]
|
|
|
|
free(keyResult);
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_server_onUpgrade_digestWriteUnauth(duk_context *ctx)
|
|
{
|
|
int nargs = duk_get_top(ctx), i;
|
|
duk_push_current_function(ctx);
|
|
duk_get_prop_string(ctx, -1, "imsg");
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_IMSG2SR); // [serverResponse]
|
|
duk_get_prop_string(ctx, -1, "Digest_writeUnauthorized"); // [serverResponse][writeUnAuth]
|
|
duk_swap_top(ctx, -2); // [writeUnAuth][this]
|
|
for (i = 0; i < nargs; ++i)
|
|
{
|
|
duk_dup(ctx, i); // [writeUnAuth][this][...]
|
|
}
|
|
duk_call_method(ctx, nargs);
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_server_onUpgrade(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [HS]
|
|
duk_get_prop_string(ctx, -1, ILibduktape_HttpStream2HttpServer); // [HS][server]
|
|
duk_get_prop_string(ctx, -1, "emit"); // [HS][server][emit]
|
|
duk_swap_top(ctx, -2); // [HS][emit][this]
|
|
duk_push_string(ctx, "upgrade"); // [HS][emit][this][upgrade]
|
|
duk_dup(ctx, 0); // [HS][emit][this][upgrade][imsg]
|
|
duk_get_prop_string(ctx, -5, ILibDuktape_HTTP2PipedReadable); // [HS][emit][this][upgrade][imsg][sck]
|
|
duk_push_c_function(ctx, ILibDuktape_HttpStream_http_server_upgradeWebsocket, DUK_VARARGS); // [HS][emit][this][upgrade][imsg][sck][func]
|
|
duk_dup(ctx, -3); // [HS][emit][this][upgrade][imsg][sck][func][imsg]
|
|
duk_put_prop_string(ctx, -2, "imsg"); // [HS][emit][this][upgrade][imsg][sck][func]
|
|
duk_put_prop_string(ctx, -2, "upgradeWebSocket"); // [HS][emit][this][upgrade][imsg][sck]
|
|
duk_push_c_function(ctx, ILibDuktape_HttpStream_http_server_onUpgrade_digestWriteUnauth, DUK_VARARGS);
|
|
duk_dup(ctx, -3); // [HS][emit][this][upgrade][imsg][sck][func][imsg]
|
|
duk_put_prop_string(ctx, -2, "imsg"); // [HS][emit][this][upgrade][imsg][sck][func]
|
|
duk_put_prop_string(ctx, -2, "Digest_writeUnauthorized"); // [HS][emit][this][upgrade][imsg][sck]
|
|
duk_push_null(ctx); // [HS][emit][this][upgrade][imsg][sck][head]
|
|
duk_get_prop_string(ctx, -3, "headers");
|
|
duk_pop(ctx);
|
|
duk_call_method(ctx, 4); duk_pop(ctx); // [HS]
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_server_onConnectionTimeout(duk_context *ctx)
|
|
{
|
|
void *cb = NULL;
|
|
duk_push_this(ctx); // [socket]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Socket2HttpServer); // [socket][HttpServer]
|
|
if ((cb = Duktape_GetHeapptrProperty(ctx, -1, ILibDuktape_HttpServer_TimeoutCB)) != NULL)
|
|
{
|
|
// Callback was specified, so the callback MUST explictly handle the situation
|
|
duk_push_heapptr(ctx, cb); // [socket][HttpServer][func]
|
|
duk_swap_top(ctx, -2); // [socket][func][this]
|
|
duk_dup(ctx, -3); // [socket][func][this][socket]
|
|
duk_call_method(ctx, 1); duk_pop_2(ctx); // ...
|
|
}
|
|
else
|
|
{
|
|
// No callback was specified, so the timed out socket MUST be closed.
|
|
duk_pop(ctx); // [socket]
|
|
duk_get_prop_string(ctx, -1, "end"); // [socket][end]
|
|
duk_swap_top(ctx, -2); // [end][this]
|
|
duk_call_method(ctx, 0); duk_pop(ctx); // ...
|
|
}
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_parseError(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [httpStream]
|
|
ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_HTTPStream2Data);
|
|
|
|
ILibDuktape_HttpStream_ServerResponse_PUSH(ctx, data->DS->writableStream->pipedReadable, NULL, data->DS->ParentObject); // [httpStream][response]
|
|
duk_dup(ctx, -1); duk_put_prop_string(ctx, -3, "\xFF_tmpServerResponse");
|
|
duk_get_prop_string(ctx, -1, "writeHead"); // [httpStream][response][writeHead]
|
|
duk_dup(ctx, -2); // [httpStream][response][writeHead][this]
|
|
duk_dup(ctx, 0); // [httpStream][response][writeHead][this][statusCode]
|
|
duk_dup(ctx, 1); // [httpStream][response][writeHead][this][statusCode][statusMessage]
|
|
duk_push_object(ctx); // [httpStream][response][writeHead][this][statusCode][statusMessage][headers]
|
|
duk_push_string(ctx, "close"); duk_put_prop_string(ctx, -2, "Connection");
|
|
|
|
duk_call_method(ctx, 3); duk_pop(ctx); // [httpStream][response]
|
|
duk_get_prop_string(ctx, -1, "end"); // [httpStream][response][end]
|
|
duk_swap_top(ctx, -2); // [httpStream][end][this]
|
|
duk_call_method(ctx, 0); duk_pop(ctx); // [httpStream]
|
|
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_server_onConnection(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [NS]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_NS2HttpServer); // [NS][HttpServer]
|
|
|
|
// Check to see if we need to set a timeout
|
|
duk_get_prop_string(ctx, -1, "timeout"); // [NS][HttpServer][timeout]
|
|
if (duk_is_number(ctx, -1))
|
|
{
|
|
duk_dup(ctx, 0); // [NS][HttpServer][timeout][socket]
|
|
duk_dup(ctx, -3); // [NS][HttpServer][timeout][socket][HttpServer]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_Socket2HttpServer); // [NS][HttpServer][timeout][socket]
|
|
duk_get_prop_string(ctx, -1, "setTimeout"); // [NS][HttpServer][timeout][socket][setTimeout]
|
|
duk_swap_top(ctx, -2); // [NS][HttpServer][timeout][setTimeout][this]
|
|
duk_get_int(ctx, -3); // [NS][HttpServer][timeout][setTimeout][this][value]
|
|
duk_push_c_function(ctx, ILibDuktape_HttpStream_http_server_onConnectionTimeout, DUK_VARARGS); // [setTimeout][this][value][callback]
|
|
duk_call_method(ctx, 2); duk_pop(ctx); // [NS][HttpServer][timeout]
|
|
}
|
|
duk_pop_2(ctx); // [NS]
|
|
|
|
// Pipe: Socket => HttpStream
|
|
duk_dup(ctx, 0); // [NS][socket]
|
|
duk_push_string(ctx, "server"); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_FinalizerDebugMessage);
|
|
duk_get_prop_string(ctx, -1, "pipe"); // [NS][socket][pipe]
|
|
duk_dup(ctx, -2); // [NS][socket][pipe][this]
|
|
|
|
duk_eval_string(ctx, "require('http').createStream(true);"); // [NS][socket][pipe][this][httpStream]
|
|
duk_push_string(ctx, "server"); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_FinalizerDebugMessage);
|
|
duk_get_prop_string(ctx, -5, ILibDuktape_NS2HttpServer); // [NS][socket][pipe][this][httpStream][httpServer]
|
|
duk_dup(ctx, -1); // [NS][socket][pipe][this][httpStream][httpServer][dup]
|
|
duk_put_prop_string(ctx, -3, ILibduktape_HttpStream2HttpServer); // [NS][socket][pipe][this][httpStream][httpServer]
|
|
ILibDuktape_EventEmitter_ForwardEventEx(ctx, -2, -1, "checkContinue");
|
|
ILibDuktape_EventEmitter_ForwardEventEx(ctx, -2, -1, "checkExpectation");
|
|
ILibDuktape_EventEmitter_ForwardEventEx(ctx, -2, -1, "clientError");
|
|
//ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "close", -1, "close");
|
|
ILibDuktape_EventEmitter_ForwardEventEx(ctx, -2, -1, "connect");
|
|
ILibDuktape_EventEmitter_ForwardEventEx(ctx, -2, -1, "request");
|
|
|
|
//ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "connect", -1, "connect");
|
|
//ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "request", -1, "request");
|
|
ILibDuktape_EventEmitter_AddOn_Infrastructure(ctx, -2, "parseError", ILibDuktape_HttpStream_http_parseError);
|
|
|
|
|
|
if (ILibDuktape_EventEmitter_HasListenersEx(ctx, -1, "upgrade") > 0)
|
|
{
|
|
ILibDuktape_EventEmitter_AddOn_Infrastructure(ctx, -2, "upgrade", ILibDuktape_HttpStream_http_server_onUpgrade);
|
|
}
|
|
|
|
duk_pop(ctx); // [NS][socket][pipe][this][httpStream]
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // [NS][socket]
|
|
|
|
duk_pop(ctx); // [NS]
|
|
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_NS2HttpServer); // [NS][HS]
|
|
duk_get_prop_string(ctx, -1, "emit"); // [NS][HS][emit]
|
|
duk_swap_top(ctx, -2); // [NS][emit][this]
|
|
duk_push_string(ctx, "connection"); // [NS][emit][this][connection]
|
|
duk_dup(ctx, 0); // [NS][emit][this][connection][socket]
|
|
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "server.onConnection() => Error dispatching connection event "); }
|
|
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_server_listen(duk_context *ctx)
|
|
{
|
|
int nargs = duk_get_top(ctx);
|
|
|
|
duk_push_this(ctx); // [server]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Http_Server2NetServer); // [server][ns]
|
|
|
|
duk_get_prop_string(ctx, -1, "listen"); // [server][ns][listen]
|
|
duk_dup(ctx, -2); // [server][ns][listen][this]
|
|
if (nargs == 0)
|
|
{
|
|
// Nothing was specified, convert to Options
|
|
duk_push_object(ctx); // [server][ns][listen][this][options]
|
|
duk_push_int(ctx, 0); duk_put_prop_string(ctx, -2, "port");
|
|
}
|
|
else
|
|
{
|
|
if (duk_is_object(ctx, 0))
|
|
{
|
|
duk_dup(ctx, 0); // [server][ns][listen][this][options]
|
|
}
|
|
else
|
|
{
|
|
// Options weren't used, so lets convert it to Options
|
|
duk_push_object(ctx); // [server][ns][listen][this][options]
|
|
if (duk_is_number(ctx, 0))
|
|
{
|
|
duk_dup(ctx, 0); duk_put_prop_string(ctx, -2, "port");
|
|
}
|
|
else
|
|
{
|
|
return(ILibDuktape_Error(ctx, "server.listen(): Unknown parameter "));
|
|
}
|
|
}
|
|
}
|
|
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // [server]
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_setTimeout(duk_context *ctx)
|
|
{
|
|
int nargs = duk_get_top(ctx), i;
|
|
int timeout = 120000;
|
|
void *callback = NULL;
|
|
|
|
for (i = 0; i < nargs; ++i)
|
|
{
|
|
if (duk_is_number(ctx, i)) { timeout = duk_require_int(ctx, i); }
|
|
if (duk_is_function(ctx, i)) { callback = duk_require_heapptr(ctx, i); }
|
|
}
|
|
|
|
duk_push_this(ctx); // [server]
|
|
duk_push_int(ctx, timeout); // [server][timeout]
|
|
duk_put_prop_string(ctx, -2, "timeout"); // [server]
|
|
if (callback != NULL)
|
|
{
|
|
duk_push_heapptr(ctx, callback); // [server][cb]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_HttpServer_TimeoutCB); // [server]
|
|
}
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_http_server_address(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [httpServer]
|
|
if (!duk_has_prop_string(ctx, -1, ILibDuktape_Http_Server2NetServer)) { return(ILibDuktape_Error(ctx, "http.server.address(): Cannot call 'address' when listen was not called")); }
|
|
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Http_Server2NetServer); // [httpServer][NS]
|
|
duk_get_prop_string(ctx, -1, "address"); // [httpServer][NS][address]
|
|
duk_swap_top(ctx, -2); // [httpServer][address][this]
|
|
duk_call_method(ctx, 0); // [httpServer][result]
|
|
return(1);
|
|
}
|
|
|
|
duk_ret_t ILibDuktape_HttpStream_http_createServer(duk_context *ctx)
|
|
{
|
|
ILibDuktape_Http_Server *server;
|
|
|
|
int nargs = duk_get_top(ctx);
|
|
duk_push_this(ctx); // [http/s]
|
|
int isHTTPS = Duktape_GetBooleanProperty(ctx, -1, "isHTTPS", 0);
|
|
duk_pop(ctx);
|
|
|
|
duk_push_object(ctx); // [server]
|
|
duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_Http_Server)); // [server][fxBuffer]
|
|
server = (ILibDuktape_Http_Server*)Duktape_GetBuffer(ctx, -1, NULL);
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_Http_Server_FixedBuffer); // [server]
|
|
|
|
memset(server, 0, sizeof(ILibDuktape_Http_Server));
|
|
server->ctx = ctx;
|
|
|
|
ILibDuktape_WriteID(ctx, isHTTPS ? "https.server" : "http.server");
|
|
ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_Create(ctx);
|
|
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "checkContinue");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "checkExpectation");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "clientError");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "close");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "connect");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "connection");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "request");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "upgrade");
|
|
|
|
duk_push_undefined(ctx);
|
|
duk_put_prop_string(ctx, -2, "timeout");
|
|
|
|
if (nargs > 0 && duk_is_function(ctx, 0))
|
|
{
|
|
ILibDuktape_EventEmitter_AddOn(emitter, "request", duk_require_heapptr(ctx, 0));
|
|
}
|
|
|
|
ILibDuktape_CreateInstanceMethod(ctx, "close", ILibDuktape_HttpStream_http_server_close, DUK_VARARGS);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "listen", ILibDuktape_HttpStream_http_server_listen, DUK_VARARGS);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "setTimeout", ILibDuktape_HttpStream_http_setTimeout, DUK_VARARGS);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "address", ILibDuktape_HttpStream_http_server_address, 0);
|
|
|
|
|
|
// Now let's create a net.server or tls.server
|
|
if (isHTTPS)
|
|
{
|
|
duk_eval_string(ctx, "require('tls');"); // [server][tls]
|
|
}
|
|
else
|
|
{
|
|
duk_eval_string(ctx, "require('net');"); // [server][net]
|
|
}
|
|
duk_get_prop_string(ctx, -1, "createServer"); // [server][nettls][createServer]
|
|
duk_swap_top(ctx, -2); // [server][createServer][this]
|
|
|
|
if (nargs > 0 && duk_is_object(ctx, 0) && !duk_is_function(ctx, 0))
|
|
{
|
|
// Options was specified
|
|
duk_dup(ctx, 0); // [server][createServer][this][options]
|
|
}
|
|
duk_push_c_function(ctx, ILibDuktape_HttpStream_http_server_onConnection, DUK_VARARGS);
|
|
|
|
duk_call_method(ctx, (nargs > 0 && duk_is_object(ctx, 0) && !duk_is_function(ctx, 0)) ? 2 : 1); // [server][netServer]
|
|
duk_dup(ctx, -2); // [server][netServer][server]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_NS2HttpServer); // [server][netServer]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_Http_Server2NetServer); // [server]
|
|
return(1);
|
|
}
|
|
|
|
typedef struct ILibDuktape_HttpStream_DispatchWrite_data
|
|
{
|
|
ILibDuktape_HttpStream_Data *httpStream;
|
|
int bufferLen;
|
|
char buffer[];
|
|
}ILibDuktape_HttpStream_DispatchWrite_data;
|
|
|
|
duk_ret_t ILibDuktape_HttpStream_WriteSink_ChainSink_DynamicBuffer_WriteSink(duk_context *ctx)
|
|
{
|
|
char *buffer;
|
|
duk_size_t bufferLen;
|
|
int beginPointer = 0;
|
|
int PAUSE = 0;
|
|
|
|
ILibDuktape_HttpStream_Data *data;
|
|
duk_push_this(ctx); // [DynamicBuffer]
|
|
duk_get_prop_string(ctx, -1, "\xFF_HTTP");
|
|
data = (ILibDuktape_HttpStream_Data*)duk_get_pointer(ctx, -1);
|
|
|
|
buffer = (char*)Duktape_GetBuffer(ctx, 0, &bufferLen);
|
|
ILibWebClient_DataResults r = ILibWebClient_OnData(NULL, buffer, &beginPointer, (int)bufferLen, NULL, (void**)&(data->WCDO), &PAUSE);
|
|
switch (r)
|
|
{
|
|
case ILibWebClient_DataResults_OK:
|
|
if (0 == beginPointer && ILibWebClient_IsFinHeader(data->WCDO) == 0 && ((int)bufferLen - beginPointer >= data->maxHeaderSize))
|
|
{
|
|
ILibDuktape_EventEmitter_SetupEmit(ctx, data->DS->ParentObject, "parseError"); // [emit][this][parseError]
|
|
duk_push_int(ctx, 431); // [emit][this][parseError][statusCode]
|
|
duk_push_string(ctx, "Headers too large"); // [emit][this][parseError][statusCode][statusMessage]
|
|
if (duk_pcall_method(ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "httpStream: Error in Event handler for 'parseError' "); }
|
|
duk_pop(ctx); // ...
|
|
}
|
|
break;
|
|
case ILibWebClient_DataResults_InvalidRequest:
|
|
ILibDuktape_EventEmitter_SetupEmit(ctx, data->DS->ParentObject, "parseError"); // [emit][this][parseError]
|
|
duk_push_int(ctx, 400); // [emit][this][parseError][statusCode]
|
|
duk_push_string(ctx, "Bad Request"); // [emit][this][parseError][statusCode][statusMessage]
|
|
if (duk_pcall_method(ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "httpStream: Error in Event handler for 'parseError' "); }
|
|
duk_pop(ctx); // ...
|
|
case ILibWebClient_DataResults_InvalidContentLength:
|
|
ILibDuktape_EventEmitter_SetupEmit(ctx, data->DS->ParentObject, "parseError"); // [emit][this][parseError]
|
|
duk_push_int(ctx, 400); // [emit][this][parseError][statusCode]
|
|
duk_push_string(ctx, "Invalid content-length specified"); // [emit][this][parseError][statusCode][statusMessage]
|
|
if (duk_pcall_method(ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "httpStream: Error in Event handler for 'parseError' "); }
|
|
duk_pop(ctx); // ...
|
|
default:
|
|
break;
|
|
}
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_WriteSink_ChainSink_DynamicBuffer_EndSink(duk_context *ctx)
|
|
{
|
|
return(0);
|
|
}
|
|
void ILibDuktape_HttpStream_WriteSink_ChainSink(void *chain, void *user)
|
|
{
|
|
ILibDuktape_HttpStream_DispatchWrite_data *data = (ILibDuktape_HttpStream_DispatchWrite_data*)user;
|
|
duk_context *ctx = data->httpStream->DS->writableStream->ctx;
|
|
|
|
if (data->httpStream->DynamicBuffer == NULL)
|
|
{
|
|
duk_push_heapptr(ctx, data->httpStream->DS->ParentObject); // [httpStream]
|
|
if (duk_peval_string(ctx, "require('DynamicBuffer')(4096);") != 0) // [httpStream][DynamicBuffer]
|
|
{
|
|
ILibDuktape_Process_UncaughtExceptionEx(ctx, "httpStream.writeSink_chainSink->DynamicBuffer(): ");
|
|
duk_pop(ctx); // ...
|
|
return;
|
|
}
|
|
data->httpStream->DynamicBuffer = duk_get_heapptr(ctx, -1); // [httpStream][DynamicBuffer]
|
|
ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "data", ILibDuktape_HttpStream_WriteSink_ChainSink_DynamicBuffer_WriteSink);
|
|
ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "end", ILibDuktape_HttpStream_WriteSink_ChainSink_DynamicBuffer_EndSink);
|
|
duk_push_pointer(ctx, data->httpStream); // [httpStream][DynamicBuffer][ptr]
|
|
duk_put_prop_string(ctx, -2, "\xFF_HTTP"); // [httpStream][DynamicBuffer]
|
|
duk_put_prop_string(ctx, -2, "\xFF_DynamicBuffer"); // [httpStream]
|
|
duk_pop(ctx); // ...
|
|
}
|
|
|
|
duk_push_external_buffer(ctx); // [extBuffer]
|
|
duk_config_buffer(ctx, -1, data->buffer, data->bufferLen);
|
|
|
|
duk_push_heapptr(ctx, data->httpStream->DynamicBuffer); // [extBuffer][DynamicBuffer]
|
|
duk_get_prop_string(ctx, -1, "write"); // [extBuffer][DynamicBuffer][write]
|
|
duk_swap_top(ctx, -2); // [extBuffer][write][this]
|
|
duk_push_buffer_object(ctx, -3, 0, data->bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); // [extBuffer][write][this][buffer]
|
|
if (duk_pcall_method(ctx, 1) != 0) // [extBuffer][retVal]
|
|
{
|
|
ILibDuktape_Process_UncaughtExceptionEx(ctx, "httpStream.WriteSink_ChainSink->DynamicBuffer.Write(): ");
|
|
duk_pop(ctx); // [extBuffer]
|
|
}
|
|
duk_pop(ctx); // ...
|
|
free(data);
|
|
}
|
|
ILibTransport_DoneState ILibDuktape_HttpStream_WriteSink(ILibDuktape_DuplexStream *DS, char *buffer, int bufferLen, void *user)
|
|
{
|
|
ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)user;
|
|
|
|
if (ILibIsRunningOnChainThread(data->chain) == 0)
|
|
{
|
|
// We need to context switch, because the event dispatch MUST be on the chain thread
|
|
ILibDuktape_HttpStream_DispatchWrite_data *tmp = (ILibDuktape_HttpStream_DispatchWrite_data*)ILibMemory_Allocate(sizeof(ILibDuktape_HttpStream_DispatchWrite_data) + bufferLen, 0, NULL, NULL);
|
|
tmp->httpStream = data;
|
|
tmp->bufferLen = bufferLen;
|
|
memcpy_s(tmp->buffer, bufferLen, buffer, bufferLen);
|
|
Duktape_RunOnEventLoop(data->chain, duk_ctx_nonce(data->DS->readableStream->ctx), data->DS->readableStream->ctx, ILibDuktape_HttpStream_WriteSink_ChainSink, NULL, tmp);
|
|
return(ILibTransport_DoneState_INCOMPLETE);
|
|
}
|
|
|
|
duk_push_heapptr(DS->readableStream->ctx, DS->ParentObject); // [httpStream]
|
|
duk_get_prop_string(DS->readableStream->ctx, -1, "emit"); // [httpStream][emit]
|
|
duk_swap_top(DS->readableStream->ctx, -2); // [emit][this]
|
|
duk_push_string(DS->readableStream->ctx, "write"); // [emit][this][write]
|
|
if (duk_pcall_method(DS->readableStream->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(DS->readableStream->ctx, "httpStream.write(): Error dispatching 'write' event "); }
|
|
duk_pop(DS->readableStream->ctx); // ...
|
|
|
|
// We're already on Chain Thread, so we can just directly write
|
|
int beginPointer = 0;
|
|
int PAUSE = 0;
|
|
int MustBuffer = 0;
|
|
int ibp = beginPointer;
|
|
ILibDuktape_WritableStream *stream = DS->writableStream;
|
|
|
|
ILibWebClient_DataResults r = ILibWebClient_OnData(NULL, buffer, &beginPointer, (int)bufferLen, NULL, (void**)&(data->WCDO), &PAUSE);
|
|
switch (r)
|
|
{
|
|
case ILibWebClient_DataResults_OK:
|
|
if (ibp == beginPointer && ILibWebClient_IsFinHeader(data->WCDO) == 0 && (bufferLen - beginPointer >= data->maxHeaderSize))
|
|
{
|
|
ILibDuktape_EventEmitter_SetupEmit(DS->writableStream->ctx, DS->ParentObject, "parseError"); // [emit][this][parseError]
|
|
duk_push_int(DS->writableStream->ctx, 431); // [emit][this][parseError][statusCode]
|
|
duk_push_string(DS->writableStream->ctx, "Headers too large"); // [emit][this][parseError][statusCode][statusMessage]
|
|
if (duk_pcall_method(DS->writableStream->ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(DS->writableStream->ctx, "httpStream: Error in Event handler for 'parseError' "); }
|
|
duk_pop(DS->writableStream->ctx); // ...
|
|
return(ILibTransport_DoneState_ERROR);
|
|
}
|
|
break;
|
|
case ILibWebClient_DataResults_InvalidRequest:
|
|
ILibDuktape_EventEmitter_SetupEmit(DS->writableStream->ctx, DS->ParentObject, "parseError"); // [emit][this][parseError]
|
|
duk_push_int(DS->writableStream->ctx, 400); // [emit][this][parseError][statusCode]
|
|
duk_push_string(DS->writableStream->ctx, "Bad Request"); // [emit][this][parseError][statusCode][statusMessage]
|
|
if (duk_pcall_method(DS->writableStream->ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(DS->writableStream->ctx, "httpStream: Error in Event handler for 'parseError' "); }
|
|
duk_pop(DS->writableStream->ctx); // ...
|
|
return(ILibTransport_DoneState_ERROR);
|
|
case ILibWebClient_DataResults_InvalidContentLength:
|
|
ILibDuktape_EventEmitter_SetupEmit(DS->writableStream->ctx, DS->ParentObject, "parseError"); // [emit][this][parseError]
|
|
duk_push_int(DS->writableStream->ctx, 400); // [emit][this][parseError][statusCode]
|
|
duk_push_string(DS->writableStream->ctx, "Invalid content-length specified"); // [emit][this][parseError][statusCode][statusMessage]
|
|
if (duk_pcall_method(DS->writableStream->ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(DS->writableStream->ctx, "httpStream: Error in Event handler for 'parseError' "); }
|
|
duk_pop(DS->writableStream->ctx); // ...
|
|
return(ILibTransport_DoneState_ERROR);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
if ((bufferLen - beginPointer) > 0)
|
|
{
|
|
// Not all the data was consumed, so let's try to push the unprocess data back
|
|
if (stream->pipedReadable != NULL)
|
|
{
|
|
// Make a JavaScript call to readable.unshift()
|
|
duk_push_heapptr(stream->ctx, stream->pipedReadable); // [readable]
|
|
if (duk_has_prop_string(stream->ctx, -1, "unshift"))
|
|
{
|
|
duk_push_external_buffer(stream->ctx); // [readable][extBuffer]
|
|
duk_config_buffer(stream->ctx, -1, buffer + beginPointer, (int)bufferLen - beginPointer);
|
|
duk_swap_top(stream->ctx, -2); // [extBuffer][readable]
|
|
duk_get_prop_string(stream->ctx, -1, "unshift"); // [extBuffer][readable][unshift]
|
|
duk_swap_top(stream->ctx, -2); // [extBuffer][unshift][this]
|
|
duk_push_buffer_object(stream->ctx, -3, 0, (int)bufferLen - beginPointer, DUK_BUFOBJ_NODEJS_BUFFER);// [extBuffer][unshift][this][buffer]
|
|
if (duk_pcall_method(stream->ctx, 1) != 0) { MustBuffer = 1; }
|
|
duk_pop_2(stream->ctx); // ...
|
|
}
|
|
else
|
|
{
|
|
duk_pop(stream->ctx);
|
|
MustBuffer = 1;
|
|
}
|
|
}
|
|
else if (stream->pipedReadable_native != NULL && stream->pipedReadable_native->UnshiftHandler != NULL)
|
|
{
|
|
if (stream->pipedReadable_native->UnshiftHandler(stream->pipedReadable_native, (int)bufferLen - beginPointer, stream->pipedReadable_native->user) == 0)
|
|
{
|
|
MustBuffer = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MustBuffer = 1;
|
|
}
|
|
if (MustBuffer != 0)
|
|
{
|
|
// We couldn't unshift unprocessed bytes, so we have to buffer it for later
|
|
// The good news is that we are on the Chain Thread, so we can have JavaScript manage the memory
|
|
duk_push_heapptr(stream->ctx, stream->obj); // [HttpStream]
|
|
if (duk_has_prop_string(stream->ctx, -1, IILibDuktape_HTTP_HoldingQueue))
|
|
{
|
|
duk_get_prop_string(stream->ctx, -1, IILibDuktape_HTTP_HoldingQueue); // [HttpStream][Holding]
|
|
}
|
|
else
|
|
{
|
|
duk_push_array(stream->ctx); // [HttpStream][Holding]
|
|
duk_dup(stream->ctx, -1); // [HttpStream][Holding][Holding]
|
|
duk_put_prop_string(stream->ctx, -3, IILibDuktape_HTTP_HoldingQueue); // [HttpStream][Holding]
|
|
}
|
|
|
|
duk_get_prop_string(stream->ctx, -1, "push"); // [HttpStream][Holding][push]
|
|
duk_swap_top(stream->ctx, -2); // [HttpStream][push][this]
|
|
duk_push_fixed_buffer(stream->ctx, (int)bufferLen - beginPointer); // [HttpStream][push][this][buffer]
|
|
memcpy_s(Duktape_GetBuffer(stream->ctx, -1, NULL), (int)bufferLen - beginPointer, buffer + beginPointer, (int)bufferLen - beginPointer);
|
|
if (duk_pcall_method(stream->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(stream->ctx, "HttpStream.Write() -> Error calling Array.push() "); }
|
|
duk_pop_2(stream->ctx); // ...
|
|
|
|
return(ILibTransport_DoneState_INCOMPLETE);
|
|
}
|
|
else
|
|
{
|
|
// We successfully unshifted bytes, so we're done here
|
|
return(ILibTransport_DoneState_COMPLETE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Consumed All Data
|
|
return(ILibTransport_DoneState_COMPLETE);
|
|
}
|
|
}
|
|
void ILibDuktape_HttpStream_EndSink(ILibDuktape_DuplexStream *stream, void *user)
|
|
{
|
|
ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)user;
|
|
|
|
if (data->bodyStream != NULL && data->endPropagated == 0)
|
|
{
|
|
ILibDuktape_readableStream_WriteEnd(data->bodyStream);
|
|
data->endPropagated = 1;
|
|
}
|
|
}
|
|
void ILibDuktape_HttpStream_ServerResponse_WriteImplicitHeaders(void *chain, void *user)
|
|
{
|
|
int retVal;
|
|
ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State *state = (ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State*)user;
|
|
if (!ILibMemory_CanaryOK(state->serverResponseStream)) { free(user); return; }
|
|
|
|
// We are on Microstack Thread, so we can access the JS object, and write the implicit headers
|
|
duk_push_heapptr(state->ctx, state->serverResponseObj); // [SR]
|
|
duk_get_prop_string(state->ctx, -1, "writeHead"); // [SR][writeHead]
|
|
duk_swap_top(state->ctx, -2); // [writeHead][this]
|
|
duk_get_prop_string(state->ctx, -1, "statusCode"); // [writeHead][this][statusCode]
|
|
duk_get_prop_string(state->ctx, -2, "statusMessage"); // [writeHead][this][statusCode][statusMessage]
|
|
duk_get_prop_string(state->ctx, -3, ILibDuktape_SR2ImplicitHeaders); // [writeHead][this][statusCode][statusMessage][headers]
|
|
if (state->endBytes >= 0) // -1: Unknown, 0: No Data, >0: Content-Length
|
|
{
|
|
duk_push_string(state->ctx, "Content-Length"); // [writeHead][this][statusCode][statusMessage][headers][name]
|
|
duk_push_int(state->ctx, state->endBytes); // [writeHead][this][statusCode][statusMessage][headers][name][value]
|
|
duk_put_prop(state->ctx, -3); // [writeHead][this][statusCode][statusMessage][headers]
|
|
}
|
|
else
|
|
{
|
|
if (state->chunk)
|
|
{
|
|
duk_push_string(state->ctx, "Transfer-Encoding"); // [writeHead][this][statusCode][statusMessage][headers][name]
|
|
duk_push_string(state->ctx, "chunked"); // [writeHead][this][statusCode][statusMessage][headers][name][value]
|
|
duk_put_prop(state->ctx, -3); // [writeHead][this][statusCode][statusMessage][headers]
|
|
}
|
|
}
|
|
if ((retVal = duk_pcall_method(state->ctx, 3)) != 0) { ILibDuktape_Process_UncaughtExceptionEx(state->ctx, "http.serverResponse.writeImplicitHeaders(): Error "); }
|
|
duk_pop(state->ctx); // ...
|
|
|
|
if (state->bufferLen > 0 && retVal == 0)
|
|
{
|
|
duk_push_external_buffer(state->ctx); // [ext]
|
|
duk_push_heapptr(state->ctx, state->writeStream); // [ext][stream]
|
|
duk_get_prop_string(state->ctx, -1, "write"); // [ext][stream][write]
|
|
duk_swap_top(state->ctx, -2); // [ext][write][this]
|
|
|
|
if (state->endBytes > 0 || state->chunk == 0)
|
|
{
|
|
// We can just directly write the data
|
|
duk_config_buffer(state->ctx, -3, state->buffer, state->bufferLen);
|
|
duk_push_buffer_object(state->ctx, -3, 0, state->bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][write][this][buffer]
|
|
retVal = duk_pcall_method(state->ctx, 1);
|
|
duk_pop_2(state->ctx); // ...
|
|
}
|
|
else
|
|
{
|
|
// We must chunk encode the data
|
|
char *tmp = ILibMemory_AllocateA(state->bufferLen + 16);
|
|
int i = sprintf_s(tmp, state->bufferLen + 16, "%X\r\n", (unsigned int)state->bufferLen);
|
|
memcpy_s(tmp + i, state->bufferLen, state->buffer, state->bufferLen);
|
|
i += ((int)state->bufferLen + sprintf_s(tmp + i + state->bufferLen, 16 - i, "\r\n"));
|
|
duk_config_buffer(state->ctx, -3, tmp, i);
|
|
|
|
duk_push_buffer_object(state->ctx, -3, 0, i, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][write][this][buffer]
|
|
retVal = duk_pcall_method(state->ctx, 1);
|
|
duk_pop_2(state->ctx); // ...
|
|
}
|
|
}
|
|
|
|
if (retVal == 0 && chain != NULL)
|
|
{
|
|
// Since we context switched to get here, we must signal that we are done
|
|
ILibDuktape_WritableStream *WS = ILibDuktape_DuplexStream_GetNativeWritable(state->ctx, state->serverResponseObj);
|
|
if (WS != NULL)
|
|
{
|
|
ILibDuktape_WritableStream_Ready(WS);
|
|
}
|
|
}
|
|
}
|
|
int ILibDuktape_HttpStream_ServerResponse_WriteSink_Flush(struct ILibDuktape_WritableStream *stream, void *user)
|
|
{
|
|
ILibDuktape_WritableStream_Ready((struct ILibDuktape_WritableStream *)user);
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_ServerResponse_WriteSink_JS_Flushed(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [stream]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_SRUSER); // [stream][ptr]
|
|
ILibDuktape_WritableStream *WS = (ILibDuktape_WritableStream*)duk_get_pointer(ctx, -1);
|
|
|
|
ILibDuktape_WritableStream_Ready(WS);
|
|
return(0);
|
|
}
|
|
void ILibDuktape_HttpStream_ServerResponse_WriteSink_Chain(void *chain, void *user)
|
|
{
|
|
ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State *state = (ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State*)user;
|
|
|
|
// We good to go
|
|
int noDrain;
|
|
duk_push_external_buffer(state->ctx); // [ext]
|
|
duk_push_heapptr(state->ctx, state->writeStream); // [ext][stream]
|
|
duk_get_prop_string(state->ctx, -1, "write"); // [ext][stream][write]
|
|
duk_dup(state->ctx, -2); // [ext][stream][write][this]
|
|
if (state->chunk)
|
|
{
|
|
char tmp[16];
|
|
int tmpLen = sprintf_s(tmp, sizeof(tmp), "%X\r\n", (int)state->bufferLen);
|
|
duk_config_buffer(state->ctx, -4, tmp, tmpLen);
|
|
duk_push_buffer_object(state->ctx, -4, 0, tmpLen, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][stream][write][this][buffer]
|
|
if (duk_pcall_method(state->ctx, 1) != 0) { duk_pop_2(state->ctx); return; }
|
|
duk_pop(state->ctx); // [ext][stream]
|
|
duk_get_prop_string(state->ctx, -1, "write"); // [ext][stream][write]
|
|
duk_dup(state->ctx, -2); // [ext][stream][write][this]
|
|
}
|
|
duk_config_buffer(state->ctx, -4, state->buffer, state->bufferLen);
|
|
duk_push_buffer_object(state->ctx, -4, 0, state->bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][stream][write][this][buffer]
|
|
if (duk_pcall_method(state->ctx, 1) != 0) { duk_pop_2(state->ctx); return; }
|
|
noDrain = duk_get_int(state->ctx, -1);
|
|
duk_pop(state->ctx); // [ext][stream]
|
|
if (state->chunk)
|
|
{
|
|
char tmp[] = "\r\n";
|
|
duk_get_prop_string(state->ctx, -1, "write"); // [ext][stream][write]
|
|
duk_dup(state->ctx, -2); // [ext][stream][write][this]
|
|
|
|
duk_config_buffer(state->ctx, -4, tmp, 2);
|
|
duk_push_buffer_object(state->ctx, -4, 0, state->bufferLen, DUK_BUFOBJ_NODEJS_BUFFER);// [ext][stream][write][this][buffer]
|
|
if (duk_pcall_method(state->ctx, 1) != 0) { duk_pop_2(state->ctx); return; }
|
|
noDrain = duk_get_int(state->ctx, -1);
|
|
duk_pop(state->ctx); // [ext][stream]
|
|
}
|
|
if (!noDrain)
|
|
{
|
|
duk_push_pointer(state->ctx, ILibDuktape_DuplexStream_GetNativeWritable(state->ctx, state->serverResponseObj));
|
|
duk_put_prop_string(state->ctx, -2, ILibDuktape_SRUSER);
|
|
ILibDuktape_EventEmitter_AddOnceEx3(state->ctx, -1, "drain", ILibDuktape_HttpStream_ServerResponse_WriteSink_JS_Flushed);
|
|
}
|
|
else
|
|
{
|
|
ILibDuktape_WritableStream_Ready(ILibDuktape_DuplexStream_GetNativeWritable(state->ctx, state->serverResponseObj));
|
|
}
|
|
free(state);
|
|
}
|
|
ILibTransport_DoneState ILibDuktape_HttpStream_ServerResponse_WriteSink(struct ILibDuktape_WritableStream *stream, char *buffer, int bufferLen, void *user)
|
|
{
|
|
ILibDuktape_HttpStream_ServerResponse_State *state = (ILibDuktape_HttpStream_ServerResponse_State*)user;
|
|
if (state->implicitHeaderHandling)
|
|
{
|
|
state->implicitHeaderHandling = 0;
|
|
if (ILibIsRunningOnChainThread(state->chain))
|
|
{
|
|
ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State *tmp = (ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State*)ILibMemory_AllocateA(sizeof(ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State) + bufferLen);
|
|
memset(tmp, 0, sizeof(ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State));
|
|
tmp->ctx = stream->ctx;
|
|
tmp->serverResponseObj = stream->obj;
|
|
tmp->serverResponseStream = stream;
|
|
tmp->writeStream = state->writeStream;
|
|
tmp->endBytes = stream->endBytes;
|
|
tmp->chunk = state->chunkSupported;
|
|
if (bufferLen > 0) { memcpy_s(tmp->buffer, bufferLen, buffer, bufferLen); tmp->bufferLen = bufferLen; }
|
|
|
|
ILibDuktape_HttpStream_ServerResponse_WriteImplicitHeaders(NULL, tmp);
|
|
return(ILibTransport_DoneState_COMPLETE);
|
|
}
|
|
else
|
|
{
|
|
ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State *buffered = (ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State*)ILibMemory_Allocate(sizeof(ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State) + bufferLen, 0, NULL, NULL);
|
|
memset(buffered, 0, sizeof(ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State));
|
|
buffered->ctx = stream->ctx;
|
|
buffered->serverResponseObj = stream->obj;
|
|
buffered->serverResponseStream = stream;
|
|
buffered->writeStream = state->writeStream;
|
|
buffered->bufferLen = bufferLen;
|
|
buffered->endBytes = stream->endBytes;
|
|
buffered->chunk = state->chunkSupported;
|
|
if (bufferLen > 0) { memcpy_s(buffered->buffer, bufferLen, buffer, bufferLen); }
|
|
|
|
Duktape_RunOnEventLoop(state->chain, duk_ctx_nonce(state->ctx), state->ctx, ILibDuktape_HttpStream_ServerResponse_WriteImplicitHeaders, NULL, buffered);
|
|
return(ILibTransport_DoneState_INCOMPLETE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Headers were already sent, so we can just send data along
|
|
ILibTransport_DoneState retVal;
|
|
if (state->nativeWriteStream != NULL)
|
|
{
|
|
state->nativeWriteStream->OnWriteFlushEx = ILibDuktape_HttpStream_ServerResponse_WriteSink_Flush;
|
|
state->nativeWriteStream->OnWriteFlushEx_User = stream;
|
|
if (state->chunkSupported && !state->contentLengthSpecified)
|
|
{
|
|
char tmp[16];
|
|
int tmpLen = sprintf_s(tmp, sizeof(tmp), "%X\r\n", (int)bufferLen);
|
|
state->nativeWriteStream->WriteSink(state->nativeWriteStream, tmp, tmpLen, state->nativeWriteStream->WriteSink_User);
|
|
}
|
|
retVal = state->nativeWriteStream->WriteSink(state->nativeWriteStream, buffer, (int)bufferLen, state->nativeWriteStream->WriteSink_User);
|
|
if (state->chunkSupported && !state->contentLengthSpecified)
|
|
{
|
|
retVal = state->nativeWriteStream->WriteSink(state->nativeWriteStream, "\r\n", 2, state->nativeWriteStream->WriteSink_User);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Upstream is a pure ECMA Script Object
|
|
if (ILibIsRunningOnChainThread(state->chain))
|
|
{
|
|
// We good to go
|
|
int noDrain;
|
|
duk_push_external_buffer(stream->ctx); // [ext]
|
|
duk_push_heapptr(stream->ctx, state->writeStream); // [ext][stream]
|
|
duk_get_prop_string(stream->ctx, -1, "write"); // [ext][stream][write]
|
|
duk_dup(stream->ctx, -2); // [ext][stream][write][this]
|
|
if (state->chunkSupported && !state->contentLengthSpecified)
|
|
{
|
|
char tmp[16];
|
|
int tmpLen = sprintf_s(tmp, sizeof(tmp), "%X\r\n", (int)bufferLen);
|
|
duk_config_buffer(stream->ctx, -4, tmp, tmpLen);
|
|
duk_push_buffer_object(stream->ctx, -4, 0, tmpLen, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][stream][write][this][buffer]
|
|
if (duk_pcall_method(stream->ctx, 1) != 0) { duk_pop_2(stream->ctx); return(ILibTransport_DoneState_ERROR); }
|
|
duk_pop(stream->ctx); // [ext][stream]
|
|
duk_get_prop_string(stream->ctx, -1, "write"); // [ext][stream][write]
|
|
duk_dup(stream->ctx, -2); // [ext][stream][write][this]
|
|
}
|
|
duk_config_buffer(stream->ctx, -4, buffer, bufferLen);
|
|
duk_push_buffer_object(stream->ctx, -4, 0, bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][stream][write][this][buffer]
|
|
if (duk_pcall_method(stream->ctx, 1) != 0) { duk_pop_2(stream->ctx); return(ILibTransport_DoneState_ERROR); }
|
|
noDrain = duk_get_int(stream->ctx, -1);
|
|
duk_pop(stream->ctx); // [ext][stream]
|
|
if (state->chunkSupported && !state->contentLengthSpecified)
|
|
{
|
|
char tmp[] = "\r\n";
|
|
duk_get_prop_string(stream->ctx, -1, "write"); // [ext][stream][write]
|
|
duk_dup(stream->ctx, -2); // [ext][stream][write][this]
|
|
|
|
duk_config_buffer(stream->ctx, -4, tmp, 2);
|
|
duk_push_buffer_object(stream->ctx, -4, 0, bufferLen, DUK_BUFOBJ_NODEJS_BUFFER);// [ext][stream][write][this][buffer]
|
|
if (duk_pcall_method(stream->ctx, 1) != 0) { duk_pop_2(stream->ctx); return(ILibTransport_DoneState_ERROR); }
|
|
noDrain = duk_get_int(stream->ctx, -1);
|
|
duk_pop(stream->ctx); // [ext][stream]
|
|
}
|
|
if (!noDrain)
|
|
{
|
|
duk_push_pointer(stream->ctx, stream);
|
|
duk_put_prop_string(stream->ctx, -2, ILibDuktape_SRUSER);
|
|
ILibDuktape_EventEmitter_AddOnceEx3(stream->ctx, -1, "drain", ILibDuktape_HttpStream_ServerResponse_WriteSink_JS_Flushed);
|
|
retVal = ILibTransport_DoneState_INCOMPLETE;
|
|
}
|
|
else
|
|
{
|
|
retVal = ILibTransport_DoneState_COMPLETE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Gotta context switch
|
|
ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State *data = (ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State*)ILibMemory_Allocate(sizeof(ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State) + bufferLen, 0, NULL, NULL);
|
|
data->chunk = state->chunkSupported;
|
|
data->ctx = stream->ctx;
|
|
data->endBytes = stream->endBytes;
|
|
data->serverResponseObj = stream->obj;
|
|
data->serverResponseStream = stream;
|
|
data->writeStream = state->writeStream;
|
|
data->bufferLen = bufferLen;
|
|
memcpy_s(data->buffer, bufferLen, buffer, bufferLen);
|
|
Duktape_RunOnEventLoop(state->chain, duk_ctx_nonce(state->ctx), state->ctx, ILibDuktape_HttpStream_ServerResponse_WriteSink_Chain, NULL, data);
|
|
return(ILibTransport_DoneState_INCOMPLETE);
|
|
}
|
|
}
|
|
return(retVal);
|
|
}
|
|
}
|
|
void ILibDuktape_HttpStream_ServerResponse_EndSink_Chain(void *chain, void *user)
|
|
{
|
|
struct ILibDuktape_WritableStream *stream = (struct ILibDuktape_WritableStream*)user;
|
|
|
|
duk_push_heapptr(stream->ctx, stream->obj); // [serverResponse]
|
|
duk_get_prop_string(stream->ctx, -1, "writeHead"); // [serverResponse][writeHead]
|
|
duk_swap_top(stream->ctx, -2); // [writeHead][this]
|
|
duk_get_prop_string(stream->ctx, -1, "statusCode"); // [writeHead][this][statusCode]
|
|
duk_get_prop_string(stream->ctx, -2, "statusMessage"); // [writeHead][this][statusCode][statusMessage]
|
|
duk_get_prop_string(stream->ctx, -3, ILibDuktape_SR2ImplicitHeaders); // [writeHead][this][statusCode][statusMessage][headers]
|
|
duk_push_string(stream->ctx, "Content-Length"); // [writeHead][this][statusCode][statusMessage][headers][name]
|
|
duk_push_int(stream->ctx, 0); // [writeHead][this][statusCode][statusMessage][headers][name][value]
|
|
duk_put_prop(stream->ctx, -3); // [writeHead][this][statusCode][statusMessage][headers]
|
|
if (duk_pcall_method(stream->ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(stream->ctx, "http.serverResponse.end(): Error writing implicit headers "); }
|
|
duk_pop(stream->ctx); // ...
|
|
|
|
// Need to reset HttpStream
|
|
ILibDuktape_serverResponse_resetHttpStream(stream->ctx, stream->obj);
|
|
}
|
|
void ILibDuktape_HttpStream_ServerResponse_EndSink_ZeroChunk_Chain(void *chain, void *user)
|
|
{
|
|
ILibDuktape_HttpStream_ServerResponse_State *state = (ILibDuktape_HttpStream_ServerResponse_State*)user;
|
|
|
|
if (state->chunkSupported && !state->contentLengthSpecified)
|
|
{
|
|
// Send zero size chunk
|
|
char tmp[] = "0\r\n\r\n";
|
|
if (state->nativeWriteStream != NULL)
|
|
{
|
|
state->nativeWriteStream->OnWriteFlushEx = NULL;
|
|
state->nativeWriteStream->OnWriteFlushEx_User = NULL;
|
|
state->nativeWriteStream->WriteSink(state->nativeWriteStream, tmp, 5, state->nativeWriteStream->WriteSink_User);
|
|
}
|
|
else
|
|
{
|
|
duk_push_external_buffer(state->ctx);
|
|
duk_config_buffer(state->ctx, -1, tmp, 5); // [ext]
|
|
duk_push_heapptr(state->ctx, state->writeStream); // [ext][stream]
|
|
duk_get_prop_string(state->ctx, -1, "write"); // [ext][stream][write]
|
|
duk_swap_top(state->ctx, -2); // [ext][write][this]
|
|
duk_push_buffer_object(state->ctx, -3, 0, 5, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][write][this][buffer]
|
|
if (duk_pcall_method(state->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(state->ctx, "http.serverResponse.end(): Error writing upstream "); }
|
|
duk_pop_2(state->ctx); // ...
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!state->chunkSupported)
|
|
{
|
|
// Close the connection
|
|
if (state->nativeWriteStream != NULL)
|
|
{
|
|
state->nativeWriteStream->EndSink(state->nativeWriteStream, state->nativeWriteStream->WriteSink_User);
|
|
}
|
|
else
|
|
{
|
|
duk_push_heapptr(state->ctx, state->writeStream); // [stream]
|
|
duk_get_prop_string(state->ctx, -1, "end"); // [stream][end]
|
|
duk_swap_top(state->ctx, -2); // [end][this]
|
|
if (duk_pcall_method(state->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(state->ctx, "http.serverResponse.end(): Error ending upstream "); }
|
|
duk_pop(state->ctx); // ...
|
|
}
|
|
}
|
|
}
|
|
|
|
// Need to reset HttpStream
|
|
ILibDuktape_serverResponse_resetHttpStream(state->ctx, state->serverResponse);
|
|
}
|
|
void ILibDuktape_HttpStream_ServerResponse_EndSink(struct ILibDuktape_WritableStream *stream, void *user)
|
|
{
|
|
ILibDuktape_HttpStream_ServerResponse_State *state = (ILibDuktape_HttpStream_ServerResponse_State*)user;
|
|
|
|
if (state->implicitHeaderHandling)
|
|
{
|
|
if (ILibIsRunningOnChainThread(state->chain))
|
|
{
|
|
duk_push_this(stream->ctx); // [serverResponse]
|
|
duk_get_prop_string(stream->ctx, -1, "writeHead"); // [serverResponse][writeHead]
|
|
duk_swap_top(stream->ctx, -2); // [writeHead][this]
|
|
duk_get_prop_string(stream->ctx, -1, "statusCode"); // [writeHead][this][statusCode]
|
|
duk_get_prop_string(stream->ctx, -2, "statusMessage"); // [writeHead][this][statusCode][statusMessage]
|
|
duk_get_prop_string(stream->ctx, -3, ILibDuktape_SR2ImplicitHeaders); // [writeHead][this][statusCode][statusMessage][headers]
|
|
duk_push_string(stream->ctx, "Content-Length"); // [writeHead][this][statusCode][statusMessage][headers][name]
|
|
duk_push_int(stream->ctx, 0); // [writeHead][this][statusCode][statusMessage][headers][name][value]
|
|
duk_put_prop(stream->ctx, -3); // [writeHead][this][statusCode][statusMessage][headers]
|
|
if (duk_pcall_method(stream->ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(stream->ctx, "http.serverResponse.end(): Error writing implicit headers "); }
|
|
duk_pop(stream->ctx); // ...
|
|
|
|
// Need to reset HttpStream
|
|
ILibDuktape_serverResponse_resetHttpStream(stream->ctx, stream->obj);
|
|
}
|
|
else
|
|
{
|
|
// Need to context switch before sending Implicit Headers
|
|
Duktape_RunOnEventLoop(state->chain, duk_ctx_nonce(stream->ctx), stream->ctx, ILibDuktape_HttpStream_ServerResponse_EndSink_Chain, NULL, stream);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Headers already sent...
|
|
if (state->nativeWriteStream != NULL)
|
|
{
|
|
ILibDuktape_HttpStream_ServerResponse_EndSink_ZeroChunk_Chain(state->chain, state);
|
|
}
|
|
else
|
|
{
|
|
Duktape_RunOnEventLoop(state->chain, duk_ctx_nonce(stream->ctx), stream->ctx, ILibDuktape_HttpStream_ServerResponse_EndSink_ZeroChunk_Chain, NULL, state);
|
|
}
|
|
}
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_ServerResponse_writeHead(duk_context *ctx)
|
|
{
|
|
int nargs = duk_get_top(ctx);
|
|
int statusCode = duk_require_int(ctx, 0);
|
|
char *statusMessage = NULL;
|
|
duk_size_t statusMessageLen = 0;
|
|
void *headers = NULL;
|
|
int i;
|
|
int contentLengthSpecified = 0;
|
|
|
|
char *buffer = NULL;
|
|
duk_size_t bufferLen = 0, len = 0;
|
|
if (statusCode < 100 || statusCode > 999) { return(ILibDuktape_Error(ctx, "http.serverResponse.writeHead(): Invalid status code")); }
|
|
|
|
for (i = 1; i < nargs; ++i)
|
|
{
|
|
if (duk_is_string(ctx, i)) { statusMessage = (char*)duk_get_lstring(ctx, i, &statusMessageLen); }
|
|
if (duk_is_object(ctx, i)) { headers = duk_require_heapptr(ctx, i); }
|
|
}
|
|
|
|
if (statusMessage == NULL)
|
|
{
|
|
switch (statusCode)
|
|
{
|
|
case 100:
|
|
statusMessage = "Continue";
|
|
statusMessageLen = 8;
|
|
break;
|
|
case 200:
|
|
statusMessage = "OK";
|
|
statusMessageLen = 2;
|
|
break;
|
|
case 400:
|
|
statusMessage = "Bad Request";
|
|
statusMessageLen = 11;
|
|
break;
|
|
case 401:
|
|
statusMessage = "Unauthorized";
|
|
statusMessageLen = 12;
|
|
break;
|
|
case 404:
|
|
statusMessage = "Not Found";
|
|
statusMessageLen = 9;
|
|
break;
|
|
case 500:
|
|
statusMessage = "Internal Server Error";
|
|
statusMessageLen = 21;
|
|
break;
|
|
default:
|
|
statusMessage = "Unspecified";
|
|
statusMessageLen = 11;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 2; ++i)
|
|
{
|
|
if (buffer == NULL)
|
|
{
|
|
bufferLen = 15 + statusMessageLen; //'HTTP/1.1 XXX statusMessage\r\n'
|
|
}
|
|
else
|
|
{
|
|
len += sprintf_s(buffer + len, bufferLen - len, "HTTP/1.1 %d %s\r\n", statusCode, statusMessage);
|
|
}
|
|
if (headers != NULL)
|
|
{
|
|
duk_push_heapptr(ctx, headers); // [headers]
|
|
duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); // [headers][enum]
|
|
while (duk_next(ctx, -1, 1)) // [headers][enum][key][value]
|
|
{
|
|
char *key, *value;
|
|
duk_size_t keyLen, valueLen;
|
|
key = (char*)duk_get_lstring(ctx, -2, &keyLen);
|
|
if (duk_is_string(ctx, -1))
|
|
{
|
|
value = (char*)duk_get_lstring(ctx, -1, &valueLen);
|
|
}
|
|
else
|
|
{
|
|
duk_get_prop_string(ctx, -1, "toString"); // [key][value][toString]
|
|
duk_swap_top(ctx, -2); // [key][toString][this]
|
|
duk_call_method(ctx, 0); // [key][valueString]
|
|
value = (char*)duk_get_lstring(ctx, -1, &valueLen);
|
|
}
|
|
if (buffer == NULL)
|
|
{
|
|
bufferLen += (keyLen + 2 + valueLen + 2); // key: value\r\n
|
|
if (keyLen == 14 && strncasecmp(key, "Content-Length", 14) == 0) { contentLengthSpecified = 1; }
|
|
}
|
|
else
|
|
{
|
|
len += sprintf_s(buffer + len, bufferLen - len, "%s: %s\r\n", key, value);
|
|
}
|
|
duk_pop_2(ctx); // [headers][enum]
|
|
}
|
|
}
|
|
if (buffer == NULL)
|
|
{
|
|
bufferLen += 2; // End of Headers
|
|
}
|
|
else
|
|
{
|
|
len += sprintf_s(buffer + len, bufferLen - len, "\r\n");
|
|
}
|
|
if (buffer == NULL) { ++bufferLen; buffer = ILibMemory_AllocateA(bufferLen); }
|
|
}
|
|
|
|
duk_push_this(ctx); // [SR]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_SR2State); // [SR][state]
|
|
((ILibDuktape_HttpStream_ServerResponse_State*)Duktape_GetBuffer(ctx, -1, NULL))->implicitHeaderHandling = 0;
|
|
((ILibDuktape_HttpStream_ServerResponse_State*)Duktape_GetBuffer(ctx, -1, NULL))->contentLengthSpecified = contentLengthSpecified;
|
|
duk_pop(ctx); // [SR]
|
|
|
|
|
|
duk_push_external_buffer(ctx); // [SR][ext]
|
|
duk_config_buffer(ctx, -1, buffer, bufferLen-1);
|
|
duk_get_prop_string(ctx, -2, ILibDuktape_SR2WS); // [SR][ext][WS]
|
|
duk_get_prop_string(ctx, -1, "write"); // [SR][ext][WS][write]
|
|
duk_swap_top(ctx, -2); // [SR][ext][write][this]
|
|
duk_push_buffer_object(ctx, -3, 0, bufferLen-1, DUK_BUFOBJ_NODEJS_BUFFER);// [SR][ext][write][this][buffer]
|
|
|
|
duk_call_method(ctx, 1); // [SR][ext][retVal]
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_ServerResponse_setHeader(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [SR]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_SR2ImplicitHeaders); // [SR][headers]
|
|
duk_dup(ctx, 0); // [SR][headers][name]
|
|
duk_dup(ctx, 1); // [SR][headers][name][value]
|
|
duk_put_prop(ctx, -3); // [SR][headers]
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_ServerResponse_removeHeader(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [SR]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_SR2ImplicitHeaders); // [SR][headers]
|
|
duk_dup(ctx, 0); // [SR][headers][name]
|
|
duk_del_prop(ctx, -2); // [SR][headers]
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_ServerResponse_Digest_SendUnauthorized(duk_context *ctx)
|
|
{
|
|
int nargs = duk_get_top(ctx);
|
|
long long nonceExpiration = ILibGetUptime() + (long long)(900000); // 15 minutes
|
|
char nonce[33];
|
|
char opaque[17];
|
|
|
|
char *realm;
|
|
duk_size_t realmLen, htmlLen = 0;
|
|
void *hptr;
|
|
char *qop = NULL;
|
|
|
|
if (!duk_is_null_or_undefined(ctx, 1) && duk_is_object(ctx, 1))
|
|
{
|
|
// Options Object is present
|
|
qop = Duktape_GetStringPropertyValue(ctx, 1, "qop", NULL);
|
|
if (qop != NULL) { duk_push_sprintf(ctx, ", qop=\"%s\"", qop); qop = (char*)duk_get_string(ctx, -1); }
|
|
}
|
|
|
|
if (nargs > 0)
|
|
{
|
|
duk_get_lstring(ctx, 1, &htmlLen);
|
|
}
|
|
|
|
duk_push_this(ctx); // [serverResponse]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_SR2HttpStream); // [serverResponse][httpStream]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2HTTP); // [serverResponse][httpStream][http]
|
|
hptr = duk_get_heapptr(ctx, -1);
|
|
|
|
util_tohex((char*)&nonceExpiration, 8, opaque);
|
|
realm = (char*)duk_get_lstring(ctx, 0, &realmLen);
|
|
ILibDuktape_Digest_CalculateNonce(ctx, hptr, nonceExpiration, opaque, 16, nonce);
|
|
|
|
duk_push_this(ctx); // [serverResponse]
|
|
duk_get_prop_string(ctx, -1, "writeHead"); // [serverResponse][writeHead]
|
|
duk_swap_top(ctx, -2); // [writeHead][this]
|
|
duk_push_int(ctx, 401); // [writeHead][this][401]
|
|
duk_push_string(ctx, "Unauthorized"); // [writeHead][this][401][Unauthorized]
|
|
duk_push_object(ctx); // [writeHead][this][401][Unauthorized][headers]
|
|
duk_push_string(ctx, "WWW-Authenticate"); // [writeHead][this][401][Unauthorized][headers][name]
|
|
int wwwLen = sprintf_s(ILibScratchPad, sizeof(ILibScratchPad), "Digest realm=\"%s\", nonce=\"%s\", opaque=\"%s\"%s", realm, nonce, opaque, qop == NULL ? "" : qop);
|
|
duk_push_lstring(ctx, ILibScratchPad, wwwLen); // [writeHead][this][401][Unauthorized][headers][name][value]
|
|
duk_put_prop(ctx, -3); // [writeHead][this][401][Unauthorized][headers]
|
|
if (htmlLen > 0)
|
|
{
|
|
duk_push_string(ctx, "Content-Type"); // [writeHead][this][401][Unauthorized][headers][name]
|
|
duk_push_string(ctx, "text/html"); // [writeHead][this][401][Unauthorized][headers][name][value]
|
|
duk_put_prop(ctx, -3); // [writeHead][this][401][Unauthorized][headers]
|
|
}
|
|
duk_push_string(ctx, "Content-Length"); // [writeHead][this][401][Unauthorized][headers][name]
|
|
duk_push_int(ctx, (int)htmlLen); // [writeHead][this][401][Unauthorized][headers][name][value]
|
|
duk_put_prop(ctx, -3); // [writeHead][this][401][Unauthorized][headers]
|
|
|
|
duk_call_method(ctx, 3); duk_pop(ctx); // ...
|
|
|
|
duk_push_this(ctx); // [serverResponse]
|
|
duk_get_prop_string(ctx, -1, "end"); // [serverResponse][end]
|
|
duk_swap_top(ctx, -2); // [end][this]
|
|
if (htmlLen > 0)
|
|
{
|
|
duk_dup(ctx, 1); // [end][this][html]
|
|
}
|
|
duk_call_method(ctx, htmlLen > 0 ? 1 : 0); duk_pop(ctx); // ...
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_ServerResponse_writeContinue(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [serverResponse]
|
|
duk_get_prop_string(ctx, -1, "writeHead"); // [serverResponse][writeHead]
|
|
duk_swap_top(ctx, -2); // [writeHead][this]
|
|
duk_push_int(ctx, 100); // [writeHead][this][100]
|
|
duk_push_string(ctx, "Continue"); // [writeHead][this][100][continue]
|
|
duk_call_method(ctx, 2);
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_ServerResponse_Finalizer(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx);
|
|
duk_del_prop_string(ctx, -1, ILibDuktape_SR2HttpStream);
|
|
return(0);
|
|
}
|
|
void ILibDuktape_HttpStream_ServerResponse_PUSH(duk_context *ctx, void* writeStream, ILibHTTPPacket *header, void *httpStream)
|
|
{
|
|
ILibDuktape_HttpStream_ServerResponse_State *state;
|
|
|
|
duk_push_object(ctx); // [resp]
|
|
duk_push_heapptr(ctx, httpStream); // [resp][httpStream]
|
|
duk_dup(ctx, -1); // [resp][httpStream][dup]
|
|
duk_put_prop_string(ctx, -3, ILibDuktape_SR2HttpStream); // [resp][httpStream]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2HTTP); // [resp][httpStream][http]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_OBJID); // [resp][httpStream][http][id]
|
|
duk_remove(ctx, -2); // [resp][httpStream][id]
|
|
duk_get_prop_string(ctx, -1, "concat"); // [resp][httpStream][id][concat]
|
|
duk_swap_top(ctx, -2); // [resp][httpStream][concat][this]
|
|
duk_push_string(ctx, ".serverResponse"); // [resp][httpStream][concat][this][serverResponse]
|
|
if (duk_pcall_method(ctx, 1) != 0) { duk_pop(ctx); duk_push_string(ctx, "http[s].serverResponse"); } // [resp][httpStream][http/s.serverResponse]
|
|
duk_remove(ctx, -2); // [resp][http/s.serverResponse]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_OBJID); // [resp]
|
|
|
|
ILibDuktape_WriteID(ctx, "http.serverResponse");
|
|
state = Duktape_PushBuffer(ctx, sizeof(ILibDuktape_HttpStream_ServerResponse_State)); // [resp][state]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_SR2State); // [resp]
|
|
state->ctx = ctx;
|
|
state->serverResponse = duk_get_heapptr(ctx, -1);
|
|
state->implicitHeaderHandling = 1;
|
|
state->chain = Duktape_GetChain(ctx);
|
|
state->writeStream = writeStream;
|
|
state->nativeWriteStream = ILibDuktape_DuplexStream_GetNativeWritable(ctx, writeStream);
|
|
state->chunkSupported = header != NULL ? ILibDuktape_Headers_IsChunkSupported(header) : 0;
|
|
duk_push_object(ctx); // [resp][implicitHeaders]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_SR2ImplicitHeaders); // [resp]
|
|
|
|
duk_push_int(ctx, 200);
|
|
duk_put_prop_string(ctx, -2, "statusCode");
|
|
duk_push_string(ctx, "OK");
|
|
duk_put_prop_string(ctx, -2, "statusMessage");
|
|
|
|
duk_push_heapptr(ctx, writeStream);
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_SR2WS);
|
|
|
|
ILibDuktape_WritableStream_Init(ctx, ILibDuktape_HttpStream_ServerResponse_WriteSink, ILibDuktape_HttpStream_ServerResponse_EndSink, state);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "writeHead", ILibDuktape_HttpStream_ServerResponse_writeHead, DUK_VARARGS);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "writeContinue", ILibDuktape_HttpStream_ServerResponse_writeContinue, 0);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "setHeader", ILibDuktape_HttpStream_ServerResponse_setHeader, 2);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "removeHeader", ILibDuktape_HttpStream_ServerResponse_removeHeader, 1);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "Digest_writeUnauthorized", ILibDuktape_HttpStream_ServerResponse_Digest_SendUnauthorized, DUK_VARARGS);
|
|
|
|
ILibDuktape_EventEmitter_AddOn_Infrastructure(ctx, -1, "~", ILibDuktape_HttpStream_ServerResponse_Finalizer);
|
|
}
|
|
|
|
int ILibDuktape_Digest_IsCorrectRealmAndNonce(duk_context *ctx, void *IMSG, char* realm, int realmLen)
|
|
{
|
|
char* auth;
|
|
duk_size_t authLen;
|
|
char* userRealm = NULL;
|
|
int userRealmLen;
|
|
char* nonce = NULL;
|
|
char* opaque = NULL;
|
|
int opaqueLen, nonceLen;
|
|
long long current = ILibGetUptime();
|
|
char expiration[8];
|
|
char calculatedNonce[33];
|
|
void *DigestTable = ILibInitHashTree_CaseInSensitiveEx(ILibMemory_AllocateA(8000));
|
|
void *hptr;
|
|
|
|
duk_push_heapptr(ctx, IMSG); // [IMSG]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_IMSG2HttpStream); // [IMSG][httpStream]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2HTTP); // [IMSG][httpStream][http]
|
|
hptr = duk_get_heapptr(ctx, -1);
|
|
duk_pop_2(ctx); // [IMSG]
|
|
duk_get_prop_string(ctx, -1, "headers"); // [IMSG][headers]
|
|
auth = (char*)Duktape_GetStringPropertyValueEx(ctx, -1, "Authorization", "", &authLen);
|
|
duk_pop_2(ctx); // ...
|
|
|
|
ILibWebServer_Digest_ParseAuthenticationHeader(DigestTable, auth, (int)authLen);
|
|
ILibGetEntryEx(DigestTable, "realm", 5, (void**)&userRealm, &userRealmLen);
|
|
ILibGetEntryEx(DigestTable, "nonce", 5, (void**)&nonce, &nonceLen);
|
|
ILibGetEntryEx(DigestTable, "opaque", 6, (void**)&opaque, &opaqueLen);
|
|
|
|
if (opaque != NULL && userRealm != NULL && userRealmLen == realmLen && strncmp(userRealm, realm, realmLen) == 0)
|
|
{
|
|
// Realm is correct, now check the Nonce & Opaque Values
|
|
if (opaqueLen != 16) { return 0; } // Invalid Opaque Block
|
|
|
|
util_hexToBuf(opaque, 16, expiration);
|
|
if (((long long*)expiration)[0] < current) { return 0; } // Opaque Block Expired
|
|
|
|
ILibDuktape_Digest_CalculateNonce(ctx, hptr, ((long long*)expiration)[0], opaque, opaqueLen, calculatedNonce);
|
|
return((nonceLen == 32 && strncmp(nonce, calculatedNonce, 32)) == 0 ? 1 : 0);
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
duk_ret_t ILibDuktape_HttpStream_IncomingMessage_Digest_IsAuthenticated(duk_context *ctx)
|
|
{
|
|
// ILibWebServer_Digest_IsAuthenticated
|
|
if (!duk_is_string(ctx, 0)) { return(ILibDuktape_Error(ctx, "IsAuthenticated(): Invalid Parameter/Type")); }
|
|
duk_size_t realmLen;
|
|
char* realm = (char*)duk_get_lstring(ctx, 0, &realmLen);
|
|
int retVal;
|
|
|
|
duk_push_this(ctx); // [IMSG]
|
|
|
|
retVal = (int)ILibDuktape_Digest_IsCorrectRealmAndNonce(ctx, duk_get_heapptr(ctx, -1) , realm, (int)realmLen);
|
|
duk_push_int(ctx, retVal);
|
|
return 1;
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_IncomingMessage_Digest_GetUsername(duk_context *ctx)
|
|
{
|
|
char *auth;
|
|
duk_size_t authLen;
|
|
void *DigestTable = ILibInitHashTree_CaseInSensitiveEx(ILibMemory_AllocateA(8000));
|
|
|
|
char *username;
|
|
int usernameLen;
|
|
|
|
duk_push_this(ctx); // [IMSG]
|
|
duk_get_prop_string(ctx, -1, "headers"); // [IMSG][headers]
|
|
auth = (char*)Duktape_GetStringPropertyValueEx(ctx, -1, "Authorization", "", &authLen);
|
|
duk_pop_2(ctx); // ...
|
|
|
|
ILibWebServer_Digest_ParseAuthenticationHeader(DigestTable, auth, (int)authLen);
|
|
ILibGetEntryEx(DigestTable, "username", 8, (void**)&username, &usernameLen);
|
|
|
|
duk_push_lstring(ctx, username, usernameLen);
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_IncomingMessage_Digest_GetQOP(duk_context *ctx)
|
|
{
|
|
//
|
|
// HTTP Digest Authentication
|
|
// https://datatracker.ietf.org/doc/html/rfc7616
|
|
//
|
|
char *auth, *qop;
|
|
duk_size_t authLen;
|
|
int qopLen;
|
|
void *DigestTable = ILibInitHashTree_CaseInSensitiveEx(ILibMemory_AllocateA(8000));
|
|
|
|
duk_push_this(ctx); // [IMSG]
|
|
duk_get_prop_string(ctx, -1, "headers"); // [IMSG][headers]
|
|
auth = (char*)Duktape_GetStringPropertyValueEx(ctx, -1, "Authorization", "", &authLen);
|
|
ILibWebServer_Digest_ParseAuthenticationHeader(DigestTable, auth, (int)authLen);
|
|
duk_pop_2(ctx); // ...
|
|
|
|
ILibGetEntryEx(DigestTable, "qop", 3, (void**)&qop, &qopLen);
|
|
if (qopLen == 0)
|
|
{
|
|
duk_push_null(ctx);
|
|
}
|
|
else
|
|
{
|
|
duk_push_lstring(ctx, qop, qopLen);
|
|
}
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_IncomingMessage_Digest_ValidatePassword(duk_context *ctx)
|
|
{
|
|
//
|
|
// HTTP Digest Authentication
|
|
// https://datatracker.ietf.org/doc/html/rfc7616
|
|
//
|
|
|
|
int retVal;
|
|
char nonce[33];
|
|
char result1[33];
|
|
char result2[33];
|
|
char result3[33];
|
|
char val[16];
|
|
|
|
MD5_CTX mctx;
|
|
|
|
char *auth, *username, *password, *opaque, *response, *uri, *realm, *method, *qop, *nc, *cnonce;
|
|
duk_size_t authLen, passwordLen, methodLen;
|
|
int usernameLen, opaqueLen, responseLen, uriLen, realmLen, qopLen, ncLen, cnonceLen;
|
|
|
|
void *DigestTable = ILibInitHashTree_CaseInSensitiveEx(ILibMemory_AllocateA(8000));
|
|
void *hptr;
|
|
|
|
password = (char*)duk_get_lstring(ctx, 0, &passwordLen);
|
|
|
|
duk_push_this(ctx); // [IMSG]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_IMSG2HttpStream); // [IMSG][httpStream]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2HTTP); // [IMSG][httpStream][http]
|
|
hptr = duk_get_heapptr(ctx, -1);
|
|
duk_pop_2(ctx); // [IMSG]
|
|
duk_get_prop_string(ctx, -1, "method");
|
|
method = (char*)duk_get_lstring(ctx, -1, &methodLen);
|
|
duk_pop(ctx);
|
|
|
|
duk_get_prop_string(ctx, -1, "headers"); // [IMSG][headers]
|
|
auth = (char*)Duktape_GetStringPropertyValueEx(ctx, -1, "Authorization", "", &authLen);
|
|
ILibWebServer_Digest_ParseAuthenticationHeader(DigestTable, auth, (int)authLen);
|
|
duk_pop_2(ctx); // ...
|
|
|
|
ILibGetEntryEx(DigestTable, "username", 8, (void**)&username, &usernameLen);
|
|
ILibGetEntryEx(DigestTable, "realm", 5, (void**)&realm, &realmLen);
|
|
ILibGetEntryEx(DigestTable, "uri", 3, (void**)&uri, &uriLen);
|
|
ILibGetEntryEx(DigestTable, "response", 8, (void**)&response, &responseLen);
|
|
ILibGetEntryEx(DigestTable, "opaque", 6, (void**)&opaque, &opaqueLen);
|
|
ILibGetEntryEx(DigestTable, "qop", 3, (void**)&qop, &qopLen);
|
|
ILibGetEntryEx(DigestTable, "nc", 2, (void**)&nc, &ncLen);
|
|
ILibGetEntryEx(DigestTable, "cnonce", 6, (void**)&cnonce, &cnonceLen);
|
|
|
|
|
|
|
|
if (username == NULL || uri == NULL || password == NULL || passwordLen == 0 || response == NULL || opaqueLen != 16)
|
|
{
|
|
duk_push_false(ctx);
|
|
return(1);
|
|
}
|
|
|
|
ILibDuktape_Digest_CalculateNonce(ctx, hptr, 0, opaque, opaqueLen, nonce);
|
|
|
|
// Part 1
|
|
MD5_Init(&mctx);
|
|
MD5_Update(&mctx, username, usernameLen);
|
|
MD5_Update(&mctx, ":", 1);
|
|
MD5_Update(&mctx, realm, realmLen);
|
|
MD5_Update(&mctx, ":", 1);
|
|
MD5_Update(&mctx, password, passwordLen);
|
|
MD5_Final((unsigned char*)val, &mctx);
|
|
util_tohex_lower(val, 16, result1);
|
|
|
|
// Part 2
|
|
MD5_Init(&mctx);
|
|
MD5_Update(&mctx, method, methodLen);
|
|
MD5_Update(&mctx, ":", 1);
|
|
MD5_Update(&mctx, uri, uriLen);
|
|
if (qopLen == 8 && strncmp(qop, "auth-int", 8)==0)
|
|
{
|
|
// For QOP/auth-int, we need to add the hash of the entity body
|
|
duk_push_this(ctx); // [imsg]
|
|
duk_get_prop_string(ctx, -1, "md5"); // [imsg][md5]
|
|
duk_prepare_method_call(ctx, -1, "toString"); // [imsg][md5][toString][this]
|
|
duk_push_string(ctx, "hex"); // [imsg][md5][toString][this][hex]
|
|
duk_call_method(ctx, 1); // [imsg][md5][hexString]
|
|
duk_prepare_method_call(ctx, -1, "toLowerCase");// [imsg][md5][hexString][toLowerCase][this]
|
|
duk_call_method(ctx, 0); // [imsg][md5][hexString][lowercaseHexString]
|
|
MD5_Update(&mctx, ":", 1);
|
|
|
|
char *hexstring;
|
|
duk_size_t hexstringLen;
|
|
hexstring = (char*)duk_get_lstring(ctx, -1, &hexstringLen);
|
|
MD5_Update(&mctx, hexstring, hexstringLen);
|
|
}
|
|
MD5_Final((unsigned char*)val, &mctx);
|
|
util_tohex_lower(val, 16, result2);
|
|
|
|
// Part 3
|
|
MD5_Init(&mctx);
|
|
MD5_Update(&mctx, result1, 32);
|
|
MD5_Update(&mctx, ":", 1);
|
|
MD5_Update(&mctx, nonce, 32);
|
|
MD5_Update(&mctx, ":", 1);
|
|
|
|
if (qopLen > 0)
|
|
{
|
|
// QOP
|
|
MD5_Update(&mctx, nc, ncLen);
|
|
MD5_Update(&mctx, ":", 1);
|
|
MD5_Update(&mctx, cnonce, cnonceLen);
|
|
MD5_Update(&mctx, ":", 1);
|
|
MD5_Update(&mctx, qop, qopLen);
|
|
MD5_Update(&mctx, ":", 1);
|
|
}
|
|
|
|
MD5_Update(&mctx, result2, 32);
|
|
MD5_Final((unsigned char*)val, &mctx);
|
|
util_tohex_lower(val, 16, result3);
|
|
|
|
retVal = (responseLen == 32 && strncmp(result3, response, 32)) == 0 ? 1 : 0;
|
|
duk_push_int(ctx, retVal);
|
|
password = NULL;
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_IncomingMessage_finalizer(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx);
|
|
duk_del_prop_string(ctx, -1, ILibDuktape_IMSG2HttpStream);
|
|
return(0);
|
|
}
|
|
void ILibDuktape_HttpStream_AddHeaderDef(duk_context *ctx, struct packetheader *header)
|
|
{
|
|
duk_push_object(ctx); // [message][headers]
|
|
packetheader_field_node *node = header->FirstField;
|
|
while (node != NULL)
|
|
{
|
|
duk_push_lstring(ctx, node->Field, node->FieldLength); // [message][headers][key]
|
|
duk_push_lstring(ctx, node->FieldData, node->FieldDataLength); // [message][headers][key][value]
|
|
duk_put_prop(ctx, -3); // [message][headers]
|
|
node = node->NextField;
|
|
}
|
|
duk_put_prop_string(ctx, -2, "headers");
|
|
duk_push_lstring(ctx, header->Version, header->VersionLength); // [message][version]
|
|
duk_put_prop_string(ctx, -2, "httpVersion");
|
|
|
|
if (header->Directive != NULL)
|
|
{
|
|
duk_push_lstring(ctx, header->Directive, header->DirectiveLength); // [message][method]
|
|
duk_get_prop_string(ctx, -1, "toUpperCase"); // [message][method][toUpper]
|
|
duk_swap_top(ctx, -2); // [message][toUpper][this]
|
|
duk_call_method(ctx, 0); // [message][method]
|
|
ILibDuktape_CreateReadonlyProperty_SetEnumerable(ctx, "method", 1); // [message]
|
|
|
|
duk_push_lstring(ctx, header->DirectiveObj, header->DirectiveObjLength);// [message][url]
|
|
duk_put_prop_string(ctx, -2, "url"); // [message]
|
|
}
|
|
else
|
|
{
|
|
duk_push_int(ctx, header->StatusCode); // [message][statusCode]
|
|
duk_put_prop_string(ctx, -2, "statusCode"); // [message]
|
|
duk_push_lstring(ctx, header->StatusData, header->StatusDataLength); // [message][statusMessage]
|
|
duk_put_prop_string(ctx, -2, "statusMessage"); // [message]
|
|
}
|
|
}
|
|
|
|
void ILibDuktape_HttpStream_IncomingMessage_PUSH(duk_context *ctx, ILibHTTPPacket *header, void *httpstream)
|
|
{
|
|
duk_push_object(ctx); // [message]
|
|
if (header->Directive != NULL)
|
|
{
|
|
duk_push_lstring(ctx, header->Directive, header->DirectiveLength); // [message][METHOD]
|
|
duk_push_lstring(ctx, header->DirectiveObj, header->DirectiveObjLength);// [message][METHOD][PATH]
|
|
duk_push_sprintf(ctx, "%s %s", duk_get_string(ctx, -2), duk_get_string(ctx, -1)); //[METHOD][PATH][STRING]
|
|
duk_put_prop_string(ctx, -4, ILibDuktape_EventEmitter_FinalizerDebugMessage);//sage][METHOD][PATH]
|
|
duk_pop_2(ctx); // [message]
|
|
}
|
|
else
|
|
{
|
|
duk_push_lstring(ctx, header->StatusData, header->StatusDataLength); // [message][STATUS]
|
|
duk_push_sprintf(ctx, "%d %s", header->StatusCode, duk_get_string(ctx, -1));// [message][STATUS][STRING]
|
|
duk_put_prop_string(ctx, -3, ILibDuktape_EventEmitter_FinalizerDebugMessage);//[message][STATUS]
|
|
duk_pop(ctx); // [message]
|
|
}
|
|
|
|
ILibDuktape_CreateFinalizer(ctx, ILibDuktape_HttpStream_IncomingMessage_finalizer);
|
|
duk_push_heapptr(ctx, httpstream); // [message][httpStream]
|
|
duk_dup(ctx, -1); // [message][httpStream][dup]
|
|
duk_put_prop_string(ctx, -3, ILibDuktape_IMSG2HttpStream); // [message][httpStream]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2HTTP); // [message][httpStream][http]
|
|
duk_remove(ctx, -2); // [message][http]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_OBJID); // [message][http][id]
|
|
duk_get_prop_string(ctx, -1, "concat"); // [message][http][id][concat]
|
|
duk_swap_top(ctx, -2); // [message][http][concat][this]
|
|
duk_push_string(ctx, ".IncomingMessage"); // [message][http][concat][this][.IncomingMessage]
|
|
if (duk_pcall_method(ctx, 1) != 0) { duk_pop(ctx); duk_push_string(ctx, "http[s].IncomingMessage"); }
|
|
duk_remove(ctx, -2); // [message][http/s.IncomingMessage]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_OBJID); // [message]
|
|
|
|
ILibDuktape_HttpStream_AddHeaderDef(ctx, header);
|
|
|
|
ILibDuktape_CreateInstanceMethod(ctx, "Digest_IsAuthenticated", ILibDuktape_HttpStream_IncomingMessage_Digest_IsAuthenticated, DUK_VARARGS);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "Digest_GetUsername", ILibDuktape_HttpStream_IncomingMessage_Digest_GetUsername, 0);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "Digest_ValidatePassword", ILibDuktape_HttpStream_IncomingMessage_Digest_ValidatePassword, 1);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "Digest_GetQOP", ILibDuktape_HttpStream_IncomingMessage_Digest_GetQOP, 0);
|
|
|
|
duk_push_heapptr(ctx, httpstream); // [message][httpStream]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2Socket); // [message][httpStream][socket]
|
|
duk_remove(ctx, -2); // [message][socket]
|
|
ILibDuktape_CreateReadonlyProperty(ctx, "socket"); // [message]
|
|
}
|
|
|
|
void ILibDuktape_HttpStream_IncomingMessage_PauseSink(ILibDuktape_readableStream* sender, void *user)
|
|
{
|
|
|
|
}
|
|
void ILibDuktape_HttpStream_IncomingMessage_ResumeSink(ILibDuktape_readableStream* sender, void *user)
|
|
{
|
|
|
|
}
|
|
int ILibDuktape_HttpStream_IncomingMessage_UnshiftBytes(ILibDuktape_readableStream *sender, int unshiftBytes, void *user)
|
|
{
|
|
ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)user;
|
|
data->bodyStream_unshiftedBytes = unshiftBytes;
|
|
return(unshiftBytes);
|
|
}
|
|
void ILibDuktape_HttpStream_DispatchEnd_ABORT(void *chain, void *user)
|
|
{
|
|
free(user);
|
|
}
|
|
void ILibDuktape_HttpStream_DispatchEnd(void *chain, void *user)
|
|
{
|
|
if(ILibMemory_CanaryOK(((void**)user)[1]))
|
|
{
|
|
duk_context *ctx = (duk_context*)((void**)user)[0];
|
|
void *heapPtr = ((ILibDuktape_DuplexStream*)((void**)user)[1])->ParentObject;
|
|
((ILibDuktape_HttpStream_Data*)((void**)user)[2])->bodyStream = NULL;
|
|
|
|
duk_push_heapptr(ctx, heapPtr); // [httpStream]
|
|
duk_get_prop_string(ctx, -1, "emit"); // [httpStream][emit]
|
|
duk_swap_top(ctx, -2); // [emit][this]
|
|
duk_push_string(ctx, "error"); // [emit][this][end]
|
|
if (duk_pcall_method(ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "httpStream.onEnd(): "); }
|
|
duk_pop(ctx); // ...
|
|
}
|
|
free(user);
|
|
}
|
|
void ILibDuktape_HttpStream_ForceDisconnect(duk_context *ctx, void ** args, int argsLen)
|
|
{
|
|
duk_push_heapptr(ctx, args[0]);
|
|
duk_get_prop_string(ctx, -1, "end");
|
|
duk_swap_top(ctx, -2);
|
|
if (duk_pcall_method(ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "httpStream.OnUpgrade(): "); }
|
|
duk_pop(ctx);
|
|
}
|
|
|
|
duk_ret_t ILibDuktape_HttpStream_OnReceive_bodyStreamFinalized(duk_context *ctx)
|
|
{
|
|
ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)Duktape_GetPointerProperty(ctx, 0, ILibDuktape_IMSG2Ptr);
|
|
if (data != NULL)
|
|
{
|
|
if ((data->endPropagated == 0) && (data->bodyStream != NULL)) { ILibDuktape_readableStream_WriteEnd(data->bodyStream); }
|
|
data->endPropagated = 1;
|
|
data->bodyStream = NULL;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
void ILibDuktape_HttpStream_IMSG_EndSink2(duk_context *ctx, void ** args, int argsLen)
|
|
{
|
|
void *tmp = args[1];
|
|
if (ILibMemory_CanaryOK(tmp))
|
|
{
|
|
duk_push_heapptr(ctx, args[0]); // [IMSG]
|
|
duk_prepare_method_call(ctx, -1, "removeAllListeners"); // [IMSG][removeAll][this]
|
|
duk_pcall_method(ctx, 0); // [IMSG][ret]
|
|
duk_pop_2(ctx); // ...
|
|
}
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_IMSG_EndSink(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx);
|
|
void *t = Duktape_PushBuffer(ctx, 1); duk_put_prop_string(ctx, -2, "tmp");
|
|
void *h = duk_get_heapptr(ctx, -1);
|
|
ILibDuktape_Immediate(ctx, (void*[]) { h, t }, 2, ILibDuktape_HttpStream_IMSG_EndSink2);
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_md5(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx);
|
|
ILibDuktape_readableStream *rs = (ILibDuktape_readableStream*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_readableStream_RSPTRS);
|
|
ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)rs->user;
|
|
|
|
duk_push_external_buffer(ctx); // [buffer]
|
|
duk_config_buffer(ctx, -1, data->entityHash, sizeof(data->entityHash));
|
|
duk_push_buffer_object(ctx, -1, 0, sizeof(data->entityHash), DUK_BUFOBJ_NODEJS_BUFFER);
|
|
return(1);
|
|
}
|
|
void ILibDuktape_HttpStream_OnReceive(ILibWebClient_StateObject WebStateObject, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebClient_ReceiveStatus recvStatus, void *user1, void *user2, int *PAUSE)
|
|
{
|
|
ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)user1;
|
|
duk_context *ctx = data->DS->writableStream->ctx;
|
|
uintptr_t ctxnonce = duk_ctx_nonce(ctx);
|
|
int top = duk_get_top(ctx);
|
|
|
|
if (data->bodyStream != NULL)
|
|
{
|
|
if (endPointer > 0)
|
|
{
|
|
MD5_Update(&(data->entityHashCtx), bodyBuffer + *beginPointer, endPointer);
|
|
ILibDuktape_readableStream_WriteData(data->bodyStream, bodyBuffer + *beginPointer, endPointer);
|
|
*beginPointer = endPointer - data->bodyStream_unshiftedBytes;
|
|
data->bodyStream_unshiftedBytes = 0;
|
|
}
|
|
if (recvStatus == ILibWebClient_ReceiveStatus_Complete)
|
|
{
|
|
MD5_Final((unsigned char*)data->entityHash, &(data->entityHashCtx));
|
|
ILibDuktape_readableStream_WriteEnd(data->bodyStream);
|
|
if (ILibMemory_CanaryOK(data)) { data->bodyStream = NULL; }
|
|
}
|
|
return;
|
|
}
|
|
|
|
duk_push_heapptr(ctx, data->DS->ParentObject); // [httpStream]
|
|
duk_get_prop_string(ctx, -1, "emit"); // [httpStream][emit]
|
|
duk_swap_top(ctx, -2); // [emit][this]
|
|
|
|
if (header == NULL)
|
|
{
|
|
duk_push_string(ctx, "error"); // [emit][this][error]
|
|
if (duk_pcall_method(ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->error(): "); }
|
|
duk_pop(ctx);
|
|
return;
|
|
}
|
|
|
|
MD5_Init(&(data->entityHashCtx)); // Initialize an MD5 Hash for the Entity Body
|
|
|
|
if (header->Directive != NULL)
|
|
{
|
|
// We are a server
|
|
|
|
if (header->DirectiveLength == 7 && strncasecmp(header->Directive, "CONNECT", 7) == 0)
|
|
{
|
|
// Connect
|
|
duk_push_string(ctx, "connect"); // [emit][this][connect] // [emit][this][request]
|
|
ILibDuktape_HttpStream_IncomingMessage_PUSH(ctx, header, data->DS->ParentObject); // [emit][this][connect][imsg] // [emit][this][request][imsg]
|
|
data->bodyStream = ILibDuktape_ReadableStream_InitEx(ctx, ILibDuktape_HttpStream_IncomingMessage_PauseSink, ILibDuktape_HttpStream_IncomingMessage_ResumeSink, ILibDuktape_HttpStream_IncomingMessage_UnshiftBytes, data);
|
|
//duk_dup(ctx, -3); duk_dup(ctx, -2); // [emit][this][connect][imsg][this][imsg] // [emit][this][request][imsg][httpstream][imsg]
|
|
//duk_put_prop_string(ctx, -2, ILibDuktape_HTTPStream2IMSG); duk_pop(ctx); // [emit][this][connect][imsg]
|
|
duk_get_prop_string(ctx, -3, ILibDuktape_HTTP2PipedReadable); // [emit][this][connect][imsg][socket]
|
|
duk_prepare_method_call(ctx, -1, "unpipe"); // [unpipe][this]
|
|
duk_pcall_method(ctx, 0); duk_pop(ctx);
|
|
|
|
if (duk_pcall_method(ctx, 3) != 0)
|
|
{
|
|
ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->request(): ");
|
|
}
|
|
else
|
|
{
|
|
if (!duk_get_boolean(ctx, -1))
|
|
{
|
|
// No upgrade listener... Close connection
|
|
printf("\n\nNo Connect Listener\n");
|
|
void *imm = ILibDuktape_Immediate(ctx, (void*[]) { data->DS->writableStream->pipedReadable }, 1, ILibDuktape_HttpStream_ForceDisconnect);
|
|
duk_push_heapptr(ctx, imm);
|
|
duk_push_heapptr(ctx, data->DS->writableStream->pipedReadable);
|
|
duk_put_prop_string(ctx, -2, "r");
|
|
duk_set_top(ctx, top);
|
|
return;
|
|
}
|
|
|
|
}
|
|
duk_pop(ctx);
|
|
|
|
if (bodyBuffer != NULL && endPointer > 0)
|
|
{
|
|
ILibDuktape_readableStream_WriteData(data->bodyStream, bodyBuffer + *beginPointer, endPointer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check Headers
|
|
if (ILibGetHeaderLine(header, "Upgrade", 7) != NULL)
|
|
{
|
|
duk_push_string(ctx, "upgrade"); // [emit][this][upgrade]
|
|
ILibDuktape_HttpStream_IncomingMessage_PUSH(ctx, header, data->DS->ParentObject); // [emit][this][upgrade][imsg]
|
|
ILibDuktape_HttpStream_ServerResponse_PUSH(ctx, data->DS->writableStream->pipedReadable, header, data->DS->ParentObject); // [emit][this][request][imsg][rsp]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_IMSG2SR);
|
|
if (duk_pcall_method(ctx, 2) != 0)
|
|
{ ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->upgrade(): "); }
|
|
else
|
|
{
|
|
if (!duk_get_boolean(ctx, -1))
|
|
{
|
|
// No upgrade listener... Close connection
|
|
printf("\n\nNo Upgrade Listener\n");
|
|
void *imm = ILibDuktape_Immediate(ctx, (void*[]) { data->DS->writableStream->pipedReadable }, 1, ILibDuktape_HttpStream_ForceDisconnect);
|
|
duk_push_heapptr(ctx, imm);
|
|
duk_push_heapptr(ctx, data->DS->writableStream->pipedReadable);
|
|
duk_put_prop_string(ctx, -2, "r");
|
|
duk_pop_2(ctx);
|
|
return;
|
|
}
|
|
}
|
|
duk_pop(ctx); // ...
|
|
}
|
|
else
|
|
{
|
|
char *val;
|
|
int valLen;
|
|
|
|
val = ILibGetHeaderLineEx(header, "Expect", 6, &valLen);
|
|
if (val != NULL)
|
|
{
|
|
if (valLen == 12 && strncasecmp(val, "100-Continue", 12) == 0)
|
|
{
|
|
// Is there a listener for 'checkContinue'?
|
|
if (ILibDuktape_EventEmitter_HasListenersEx(ctx, -1, "checkContinue"))
|
|
{
|
|
duk_push_string(ctx, "checkContinue"); // [emit][this][checkContinue]
|
|
ILibDuktape_HttpStream_IncomingMessage_PUSH(ctx, header, data->DS->ParentObject); // [emit][this][checkContinue][imsg]
|
|
data->bodyStream = ILibDuktape_ReadableStream_InitEx(ctx, ILibDuktape_HttpStream_IncomingMessage_PauseSink, ILibDuktape_HttpStream_IncomingMessage_ResumeSink, ILibDuktape_HttpStream_IncomingMessage_UnshiftBytes, data);
|
|
//duk_dup(ctx, -3); duk_dup(ctx, -2); // [emit][this][checkContinue][imsg][httpstream][imsg]
|
|
//duk_put_prop_string(ctx, -2, ILibDuktape_HTTPStream2IMSG); duk_pop(ctx); // [emit][this][checkContinue][imsg]
|
|
|
|
// Setup an infrastructure event, to remove all subscribers when done
|
|
duk_events_setup_on(ctx, -1, "end", ILibDuktape_HttpStream_IMSG_EndSink);
|
|
duk_push_true(ctx); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_InfrastructureEvent);
|
|
duk_pcall_method(ctx, 2); duk_pop(ctx);
|
|
|
|
ILibDuktape_HttpStream_ServerResponse_PUSH(ctx, data->DS->writableStream->pipedReadable, header, data->DS->ParentObject); // [emit][this][checkContinue][imsg][rsp]
|
|
if (duk_pcall_method(ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->checkContinue(): "); } // [rsp][hadListener]
|
|
duk_pop(ctx); // ...
|
|
}
|
|
else
|
|
{
|
|
// Nobody listening for 'checkContinue', so we need to respond with 100 Continue
|
|
ILibDuktape_HttpStream_ServerResponse_PUSH(ctx, data->DS->writableStream->pipedReadable, header, data->DS->ParentObject); // [emit][this][rsp]
|
|
duk_get_prop_string(ctx, -1, "writeContinue"); // [emit][this][rsp][writeContinue]
|
|
duk_swap_top(ctx, -2); // [emit][this][writeContinue][this]
|
|
duk_call_method(ctx, 0); duk_pop(ctx); // [emit][this]
|
|
|
|
// Since nobody was listening for 'checkContinue', need to process this as a 'request'
|
|
duk_push_string(ctx, "request"); // [emit][this][request]
|
|
ILibDuktape_HttpStream_IncomingMessage_PUSH(ctx, header, data->DS->ParentObject); // [emit][this][request][imsg]
|
|
data->bodyStream = ILibDuktape_ReadableStream_InitEx(ctx, ILibDuktape_HttpStream_IncomingMessage_PauseSink, ILibDuktape_HttpStream_IncomingMessage_ResumeSink, ILibDuktape_HttpStream_IncomingMessage_UnshiftBytes, data);
|
|
//duk_dup(ctx, -3); duk_dup(ctx, -2); // [emit][this][request][imsg][httpstream][imsg]
|
|
//duk_put_prop_string(ctx, -2, ILibDuktape_HTTPStream2IMSG); duk_pop(ctx); // [emit][this][request][imsg]
|
|
|
|
// Setup an infrastructure event, to remove all subscribers when done
|
|
duk_events_setup_on(ctx, -1, "end", ILibDuktape_HttpStream_IMSG_EndSink);
|
|
duk_push_true(ctx); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_InfrastructureEvent);
|
|
duk_pcall_method(ctx, 2); duk_pop(ctx);
|
|
|
|
|
|
ILibDuktape_HttpStream_ServerResponse_PUSH(ctx, data->DS->writableStream->pipedReadable, header, data->DS->ParentObject); // [emit][this][request][imsg][rsp]
|
|
if (duk_pcall_method(ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->request(): "); }
|
|
duk_pop(ctx); // ...
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
duk_push_string(ctx, "request"); // [emit][this][request]
|
|
ILibDuktape_HttpStream_IncomingMessage_PUSH(ctx, header, data->DS->ParentObject); // [emit][this][request][imsg]
|
|
data->bodyStream = ILibDuktape_ReadableStream_InitEx(ctx, ILibDuktape_HttpStream_IncomingMessage_PauseSink, ILibDuktape_HttpStream_IncomingMessage_ResumeSink, ILibDuktape_HttpStream_IncomingMessage_UnshiftBytes, data);
|
|
ILibDuktape_CreateEventWithGetter_SetEnumerable(ctx, "md5", ILibDuktape_HttpStream_md5, 1);
|
|
|
|
// Setup an infrastructure event, to remove all subscribers when done
|
|
duk_events_setup_on(ctx, -1, "end", ILibDuktape_HttpStream_IMSG_EndSink);
|
|
duk_push_true(ctx); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_InfrastructureEvent);
|
|
duk_pcall_method(ctx, 2); duk_pop(ctx);
|
|
|
|
ILibDuktape_HttpStream_ServerResponse_PUSH(ctx, data->DS->writableStream->pipedReadable, header, data->DS->ParentObject); // [emit][this][request][imsg][rsp]
|
|
|
|
if (duk_pcall_method(ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->request(): "); }
|
|
duk_pop(ctx);
|
|
|
|
if (bodyBuffer != NULL && endPointer > 0)
|
|
{
|
|
MD5_Update(&(data->entityHashCtx), bodyBuffer + *beginPointer, endPointer);
|
|
ILibDuktape_readableStream_WriteData(data->bodyStream, bodyBuffer + *beginPointer, endPointer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We are a client
|
|
|
|
// First, lets check to see if 'Connection: close' was specified
|
|
char *value;
|
|
int valueLen;
|
|
value = ILibGetHeaderLineEx(header, "connection", 10, &valueLen);
|
|
if (value != NULL && valueLen == 5 && strncasecmp(value, "close", 5) == 0) { data->connectionCloseSpecified = 1; }
|
|
if (header->VersionLength == 3 && strncmp(header->Version, "1.0", 3) == 0) { if (!(value != NULL && valueLen == 10 && strncasecmp(value, "keep-alive", 10) == 0)) { data->connectionCloseSpecified = 1; } }
|
|
|
|
if (data->ConnectMethod != 0)
|
|
{
|
|
duk_push_string(ctx, "connect"); // [emit][this][connect]
|
|
ILibDuktape_HttpStream_IncomingMessage_PUSH(ctx, header, data->DS->ParentObject); // [emit][this][connect][imsg]
|
|
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->connect(): "); }
|
|
duk_pop(ctx);
|
|
return;
|
|
}
|
|
|
|
switch (header->StatusCode)
|
|
{
|
|
case 100:
|
|
duk_push_string(ctx, "continue"); // [emit][this][continue]
|
|
if (duk_pcall_method(ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive(): "); }
|
|
duk_pop(ctx);
|
|
break;
|
|
case 101:
|
|
duk_push_string(ctx, "upgrade"); // [emit][this][upgrade]
|
|
ILibDuktape_HttpStream_IncomingMessage_PUSH(ctx, header, data->DS->ParentObject); // [emit][this][upgrade][imsg]
|
|
duk_del_prop_string(ctx, -1, ILibDuktape_IMSG2HttpStream);
|
|
duk_insert(ctx, -4); // [imsg][emit][this][upgrade]
|
|
duk_dup(ctx, -4); // [imsg][emit][this][upgrade][imsg]
|
|
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->upgrade(): "); }
|
|
duk_pop(ctx);
|
|
duk_pop(ctx);
|
|
break;
|
|
default:
|
|
duk_push_string(ctx, "response"); // [emit][this][response]
|
|
ILibDuktape_HttpStream_IncomingMessage_PUSH(ctx, header, data->DS->ParentObject); // [emit][this][response][imsg]
|
|
data->bodyStream = ILibDuktape_ReadableStream_InitEx(ctx, ILibDuktape_HttpStream_IncomingMessage_PauseSink, ILibDuktape_HttpStream_IncomingMessage_ResumeSink, ILibDuktape_HttpStream_IncomingMessage_UnshiftBytes, data);
|
|
duk_push_pointer(ctx, data);
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_IMSG2Ptr);
|
|
duk_events_setup_on(ctx, -1, "end", ILibDuktape_HttpStream_IMSG_EndSink);
|
|
duk_push_true(ctx); duk_put_prop_string(ctx, -2, ILibDuktape_EventEmitter_InfrastructureEvent);
|
|
duk_pcall_method(ctx, 2); duk_pop(ctx);
|
|
|
|
//duk_dup(ctx, -3); // [emit][this][response][imsg][httpstream]
|
|
//duk_dup(ctx, -2); // [emit][this][response][imsg][httpstream][imsg]
|
|
//duk_put_prop_string(ctx, -2, ILibDuktape_HTTPStream2IMSG); // [emit][this][response][imsg][httpstream]
|
|
//duk_pop(ctx); // [emit][this][response][imsg]
|
|
|
|
ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "~", ILibDuktape_HttpStream_OnReceive_bodyStreamFinalized);
|
|
|
|
int htmpLen;
|
|
char *htmp = ILibGetHeaderLineEx(header, "Content-Encoding", 16, &htmpLen);
|
|
if (htmpLen == 4 && strncmp(htmp, "gzip", 4) == 0)
|
|
{
|
|
duk_eval_string(ctx, "require('compressed-stream').createDecompressor({WBITS:31});"); // [emit][this][response][imsg][dec]
|
|
duk_prepare_method_call(ctx, -2, "pipe"); // [emit][this][response][imsg][dec][pipe][this]
|
|
duk_dup(ctx, -3); // [emit][this][response][imsg][dec][pipe][this][dec]
|
|
duk_pcall_method(ctx, 1); // [emit][this][response][imsg][dec][ret]
|
|
duk_pop(ctx); // [emit][this][response][imsg][dec]
|
|
duk_remove(ctx, -2); // [emit][this][response][dec]
|
|
ILibDuktape_HttpStream_AddHeaderDef(ctx, header);
|
|
}
|
|
|
|
|
|
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->response(): "); }
|
|
duk_pop(ctx);
|
|
|
|
if (bodyBuffer != NULL && endPointer > 0)
|
|
{
|
|
ILibDuktape_readableStream_WriteData(data->bodyStream, bodyBuffer + *beginPointer, endPointer);
|
|
*beginPointer += endPointer;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (data->bodyStream != NULL && recvStatus == ILibWebClient_ReceiveStatus_Complete)
|
|
{
|
|
MD5_Final((unsigned char*)data->entityHash, &(data->entityHashCtx));
|
|
ILibDuktape_readableStream_WriteEnd(data->bodyStream);
|
|
data->endPropagated = 1;
|
|
}
|
|
if (recvStatus == ILibWebClient_ReceiveStatus_Complete)
|
|
{
|
|
if (ILibIsRunningOnChainThread(data->chain) != 0)
|
|
{
|
|
// We're on the Chain Thread, so we can directly emit the 'end' event
|
|
data->bodyStream = NULL;
|
|
if (data->DS == NULL) { return; }
|
|
|
|
duk_push_heapptr(ctx, data->DS->ParentObject); // [httpStream]
|
|
duk_get_prop_string(ctx, -1, "emit"); // [httpStream][emit]
|
|
duk_swap_top(ctx, -2); // [emit][this]
|
|
duk_push_string(ctx, "end"); // [emit]][this][end]
|
|
if (duk_pcall_method(ctx, 1) != 0) ILibDuktape_Process_UncaughtExceptionEx(ctx, "httpStream.onReceive(): Error dispatching 'end': %s", duk_safe_to_string(ctx, -1));
|
|
duk_pop(ctx); // ...
|
|
|
|
if (header->Directive == NULL && data->connectionCloseSpecified == 0)
|
|
{
|
|
duk_push_heapptr(ctx, data->DS->ParentObject); // [httpStream]
|
|
if (duk_has_prop_string(ctx, -1, ILibDuktape_HTTPStream2Socket))
|
|
{
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2Socket); // [httpStream][socket]
|
|
if (duk_has_prop_string(ctx, -1, ILibDuktape_Socket2Agent))
|
|
{
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Socket2Agent); // [httpStream][socket][agent]
|
|
duk_get_prop_string(ctx, -1, "keepSocketAlive"); // [httpStream][socket][agent][keepSocketAlive]
|
|
duk_swap_top(ctx, -2); // [httpStream][socket][keepSocketAlive][this]
|
|
duk_dup(ctx, -3); // [httpStream][socket][keepSocketAlive][this][socket]
|
|
if (duk_pcall_method(ctx, 1) != 0) {}
|
|
duk_pop(ctx); // [httpStream][socket]
|
|
duk_pop_2(ctx); // ...
|
|
}
|
|
else
|
|
{
|
|
duk_pop_2(ctx);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
duk_pop(ctx);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We're on the wrong thread to dispatch the 'end' event, so we have to context switch
|
|
void **tmp = (void**)ILibMemory_Allocate(3 * sizeof(void*), 0, NULL, NULL);
|
|
tmp[0] = ctx;
|
|
tmp[1] = data->DS;
|
|
tmp[2] = data;
|
|
Duktape_RunOnEventLoop(data->chain, ctxnonce, ctx, ILibDuktape_HttpStream_DispatchEnd, ILibDuktape_HttpStream_DispatchEnd_ABORT, tmp);
|
|
}
|
|
}
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_Finalizer(duk_context *ctx)
|
|
{
|
|
//duk_del_prop_string(ctx, 0, ILibDuktape_HTTPStream2IMSG);
|
|
duk_get_prop_string(ctx, 0, ILibDuktape_HTTPStream2Data);
|
|
ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)Duktape_GetBuffer(ctx, -1, NULL);
|
|
|
|
ILibWebClient_DestroyWebClientDataObject(data->WCDO);
|
|
return(0);
|
|
}
|
|
|
|
duk_ret_t ILibDuktape_HttpStream_connectionCloseSpecified(duk_context *ctx)
|
|
{
|
|
ILibDuktape_HttpStream_Data *data;
|
|
|
|
duk_push_this(ctx); // [httpstream]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2Data); // [httpstream][data]
|
|
data = (ILibDuktape_HttpStream_Data*)Duktape_GetBuffer(ctx, -1, NULL);
|
|
duk_push_boolean(ctx, data->connectionCloseSpecified);
|
|
return(1);
|
|
}
|
|
void ILibDuktape_HttpStream_piped(struct ILibDuktape_readableStream *sender, void *writableStream, void *user)
|
|
{
|
|
duk_push_heapptr(sender->ctx, sender->object); // [httpStream]
|
|
duk_push_heapptr(sender->ctx, writableStream); // [httpStream][dest]
|
|
duk_put_prop_string(sender->ctx, -2, ILibDuktape_HTTP2PipedWritable); // [httpStream]
|
|
duk_pop(sender->ctx); // ...
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_pipeEvent(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [httpStream]
|
|
duk_dup(ctx, 0); // [httpStream][readable]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_HTTP2PipedReadable); // [httpStream]
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_unpipeEvent(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [stream]
|
|
duk_del_prop_string(ctx, -1, ILibDuktape_HTTP2PipedReadable);
|
|
duk_prepare_method_call(ctx, -1, "removeAllListeners"); // [stream][remove]
|
|
duk_call_method(ctx, 0);
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibduktape_HttpStream_create(duk_context *ctx)
|
|
{
|
|
int nargs = duk_get_top(ctx);
|
|
ILibDuktape_HttpStream_Data *data;
|
|
duk_push_object(ctx); // [httpStream]
|
|
duk_push_this(ctx); // [httpStream][http]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_HTTPStream2HTTP); // [httpStream]
|
|
ILibDuktape_WriteID(ctx, "http.httpStream");
|
|
ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_Create(ctx);
|
|
data = (ILibDuktape_HttpStream_Data*)Duktape_PushBuffer(ctx, sizeof(ILibDuktape_HttpStream_Data));
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_HTTPStream2Data); // [httpStream]
|
|
|
|
data->maxHeaderSize = 4096;
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "end");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "error");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "parseError");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "continue");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "response");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "upgrade");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "checkContinue");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "checkExpectation");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "clientError");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "request");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "connect");
|
|
ILibDuktape_EventEmitter_CreateEventEx(emitter, "write");
|
|
|
|
data->DS = ILibDuktape_DuplexStream_InitEx(ctx, ILibDuktape_HttpStream_WriteSink, ILibDuktape_HttpStream_EndSink,
|
|
NULL, NULL, NULL, data);
|
|
data->DS->readableStream->PipeHookHandler = ILibDuktape_HttpStream_piped;
|
|
data->WCDO = ILibCreateWebClientEx(ILibDuktape_HttpStream_OnReceive, (ILibAsyncSocket_SocketModule)NULL, data, NULL);
|
|
if (nargs > 0 && duk_is_boolean(ctx, 0))
|
|
{
|
|
ILibWebClient_WCDO_ServerFlag(data->WCDO)[0] = duk_require_boolean(ctx, 0) ? 1 : 2;
|
|
}
|
|
else
|
|
{
|
|
ILibWebClient_WCDO_ServerFlag(data->WCDO)[0] = 2;
|
|
}
|
|
|
|
data->chain = Duktape_GetChain(ctx);
|
|
|
|
ILibDuktape_EventEmitter_AddOn_Infrastructure(ctx, -1, "pipe", ILibDuktape_HttpStream_pipeEvent);
|
|
ILibDuktape_EventEmitter_AddOn_Infrastructure(ctx, -1, "unpipe", ILibDuktape_HttpStream_unpipeEvent);
|
|
|
|
ILibDuktape_CreateEventWithGetter(ctx, "connectionCloseSpecified", ILibDuktape_HttpStream_connectionCloseSpecified);
|
|
ILibDuktape_CreateFinalizer(ctx, ILibDuktape_HttpStream_Finalizer);
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_Agent_getName(duk_context *ctx)
|
|
{
|
|
char *host = Duktape_GetStringPropertyValue(ctx, 0, "host", "127.0.0.1");
|
|
int port = Duktape_GetIntPropertyValue(ctx, 0, "port", 0);
|
|
|
|
sprintf_s(ILibScratchPad2, sizeof(ILibScratchPad2), "%s:%d:%s", host, port, Duktape_GetStringPropertyValue(ctx, 0, "localAddress", "0.0.0.0"));
|
|
|
|
duk_push_string(ctx, ILibScratchPad2);
|
|
return(1);
|
|
}
|
|
int ILibDuktape_RemoveObjFromArray(duk_context *ctx, void *arrObj, void *obj)
|
|
{
|
|
int retVal = -1;
|
|
int len, i;
|
|
duk_push_heapptr(ctx, arrObj); // [array]
|
|
len = (int)duk_get_length(ctx, -1);
|
|
for (i = 0; i < len; ++i)
|
|
{
|
|
duk_get_prop_index(ctx, -1, i); // [array][obj]
|
|
if (duk_get_heapptr(ctx, -1) == obj)
|
|
{
|
|
duk_pop(ctx); // [array]
|
|
duk_get_prop_string(ctx, -1, "splice"); // [array][splice]
|
|
duk_dup(ctx, -2); // [array][splice][this]
|
|
duk_push_int(ctx, i); // [array][splice][this][i]
|
|
duk_push_int(ctx, 1); // [array][splice][this][i][1]
|
|
duk_call_method(ctx, 2); duk_pop(ctx); // [array]
|
|
break;
|
|
}
|
|
duk_pop(ctx); // [array]
|
|
}
|
|
retVal = (int)duk_get_length(ctx, -1);
|
|
duk_pop(ctx); // ...
|
|
return(retVal);
|
|
}
|
|
void ILibDuktape_RemoveObjFromTable(duk_context *ctx, duk_idx_t tableIdx, char *key, void *obj)
|
|
{
|
|
if (duk_has_prop_string(ctx, tableIdx, key))
|
|
{
|
|
duk_get_prop_string(ctx, tableIdx, key); // [Array]
|
|
if (ILibDuktape_RemoveObjFromArray(ctx, duk_get_heapptr(ctx, -1), obj) == 0)
|
|
{
|
|
duk_pop(ctx); // ...
|
|
duk_del_prop_string(ctx, tableIdx, key);
|
|
}
|
|
else
|
|
{
|
|
duk_pop(ctx); // ...
|
|
}
|
|
}
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_Agent_socketEndSink(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [socket]
|
|
|
|
//printf("socket has closed: %p\n", duk_get_heapptr(ctx, -1));
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Socket2Agent); // [socket][agent]
|
|
duk_get_prop_string(ctx, -2, ILibDuktape_Socket2AgentKey); // [socket][agent][key]
|
|
char *key = Duktape_GetBuffer(ctx, -1, NULL);
|
|
duk_pop(ctx); // [socket][agent[]
|
|
|
|
// Check to see if this socket was in the freeSockets table
|
|
duk_get_prop_string(ctx, -1, "freeSockets"); // [socket][agent][freeSockets]
|
|
ILibDuktape_RemoveObjFromTable(ctx, -1, key, duk_get_heapptr(ctx, 0));
|
|
duk_pop(ctx); // [socket][agent]
|
|
|
|
// Check to see if this socket was in the sockets table
|
|
duk_get_prop_string(ctx, -1, "sockets"); // [socket][agent][socketsTable]
|
|
ILibDuktape_RemoveObjFromTable(ctx, -1, key, duk_get_heapptr(ctx, 0));
|
|
duk_pop(ctx); // [socket][agent]
|
|
|
|
duk_eval_string(ctx, "require('http').globalAgent.sockets;"); // [socket][agent][table]
|
|
ILibDuktape_RemoveObjFromTable(ctx, -1, key, duk_get_heapptr(ctx, 0));
|
|
duk_pop(ctx); // [socket][agent]
|
|
|
|
duk_eval_string(ctx, "require('https').globalAgent.sockets;"); // [socket][agent][table]
|
|
ILibDuktape_RemoveObjFromTable(ctx, -1, key, duk_get_heapptr(ctx, 0));
|
|
duk_pop(ctx); // [socket][agent]
|
|
|
|
// Now that we cleared this socket out of all the tables, we need to check to see if we need to create a new connection
|
|
duk_get_prop_string(ctx, -1, "requests"); // [socket][agent][requestTable]
|
|
if (duk_has_prop_string(ctx, -1, key))
|
|
{
|
|
duk_get_prop_string(ctx, -1, key); // [socket][agent][requestTable][array]
|
|
if (duk_get_length(ctx, -1) > 0)
|
|
{
|
|
// There is at least one request waiting for a socket
|
|
duk_get_prop_string(ctx, -3, "sockets"); // [socket][agent][requestTable][array][socketsTable]
|
|
duk_get_prop_string(ctx, -1, key); // [socket][agent][requestTable][array][socketsTable][array]
|
|
if (duk_is_undefined(ctx, -1) || (duk_get_length(ctx, -1) < (size_t)Duktape_GetIntPropertyValue(ctx, -5, "maxSockets", 1)))
|
|
{
|
|
// We need to create a new socket
|
|
duk_pop_n(ctx, 4); // [socket][agent]
|
|
duk_dup(ctx, -1); // [socket][agent][agent]
|
|
duk_get_prop_string(ctx, -1, "createConnection"); // [socket][agent][agent][createConnection]
|
|
duk_swap_top(ctx, -2); // [socket][agent][createConnection][this]
|
|
duk_get_prop_string(ctx, -4, "\xFF_NET_SOCKET2OPTIONS"); // [socket][agent][createConnection][this][options]
|
|
duk_push_c_function(ctx, ILibDuktape_HttpStream_http_OnConnect, DUK_VARARGS); // We need to register here, because TLS/NonTLS have different event names
|
|
duk_call_method(ctx, 2); // [socket][agent][newsocket]
|
|
duk_swap_top(ctx, -2); // [socket][newsocket][agent]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_Socket2Agent); // [socket][newsocket]
|
|
ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "error", ILibDuktape_HttpStream_http_OnConnectError);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_Agent_keepSocketAlive_timeout(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [socket]
|
|
duk_get_prop_string(ctx, -1, "end"); // [socket][end]
|
|
duk_swap_top(ctx, -2); // [end][this]
|
|
duk_call_method(ctx, 0);
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_Agent_keepSocketAlive(duk_context *ctx)
|
|
{
|
|
int retVal = 0;
|
|
char *remoteAddress = Duktape_GetStringPropertyValue(ctx, 0, "remoteAddress", "127.0.0.1");
|
|
char *remoteHost = Duktape_GetStringPropertyValue(ctx, 0, "remoteHost", remoteAddress);
|
|
char *key;
|
|
|
|
if (duk_has_prop_string(ctx, 0, ILibDuktape_Socket2AgentKey))
|
|
{
|
|
key = (char*)Duktape_GetStringPropertyValue(ctx, 0, ILibDuktape_Socket2AgentKey, NULL);
|
|
}
|
|
else
|
|
{
|
|
duk_push_this(ctx); // [Agent]
|
|
duk_get_prop_string(ctx, -1, "getName"); // [Agent][getName]
|
|
duk_swap_top(ctx, -2); // [getName][this]
|
|
duk_push_object(ctx); // [getName][this][options]
|
|
duk_push_string(ctx, remoteHost); // [getName][this][options][host]
|
|
duk_put_prop_string(ctx, -2, "host"); // [getName][this][options]
|
|
duk_get_prop_string(ctx, 0, "remotePort"); // [getName][this][options][port]
|
|
duk_put_prop_string(ctx, -2, "port"); // [getName][this][options]
|
|
duk_call_method(ctx, 1); // [key]
|
|
key = (char*)duk_get_string(ctx, -1);
|
|
}
|
|
|
|
duk_push_this(ctx); // [key][Agent]
|
|
if (duk_has_prop_string(ctx, 0, ILibDuktape_Agent_SocketJustCreated))
|
|
{
|
|
duk_del_prop_string(ctx, 0, ILibDuktape_Agent_SocketJustCreated);
|
|
}
|
|
else if (Duktape_GetBooleanProperty(ctx, -1, "keepAlive", 0) == 0)
|
|
{
|
|
duk_get_prop_string(ctx, 0, "end"); // [end]
|
|
duk_dup(ctx, 0); // [end][this]
|
|
duk_call_method(ctx, 0);
|
|
return(0);
|
|
}
|
|
|
|
|
|
duk_get_prop_string(ctx, -1, "requests"); // [key][Agent][requests]
|
|
//ILibDuktape_Log_Object(ctx, -1, "Agent/Requests");
|
|
|
|
if (duk_has_prop_string(ctx, -1, key))
|
|
{
|
|
// Has Key, check the Array
|
|
duk_get_prop_string(ctx, -1, key); // [key][Agent][requests][Array]
|
|
//ILibDuktape_Log_Object(ctx, -1, "Agent/Request/ArrayIndex");
|
|
|
|
duk_get_prop_string(ctx, -1, "shift"); // [key][Agent][requests][Array][shift]
|
|
duk_swap_top(ctx, -2); // [key][Agent][requests][shift][this]
|
|
duk_call_method(ctx, 0); // [key][Agent][requests][request]
|
|
if (!duk_is_undefined(ctx, -1))
|
|
{
|
|
// There was a request
|
|
duk_push_this(ctx); // [key][Agent][requests][request][Agent]
|
|
duk_get_prop_string(ctx, -1, "reuseSocket");// [key][Agent][requests][request][Agent][reuseSocket]
|
|
duk_swap_top(ctx, -2); // [key][Agent][requests][request][reuseSocket][this]
|
|
duk_dup(ctx, 0); // [key][Agent][requests][request][reuseSocket][this][socket]
|
|
duk_dup(ctx, -4); // [key][Agent][requests][request][reuseSocket][this][socket][request]
|
|
duk_call_method(ctx, 2); // [key][Agent][requests][request][retVal]
|
|
retVal = 1;
|
|
duk_pop(ctx); // [key][Agent][requests][request]
|
|
}
|
|
}
|
|
if (retVal == 0)
|
|
{
|
|
// No Requests Found
|
|
duk_push_this(ctx); // [Agent]
|
|
duk_get_prop_string(ctx, -1, "freeSockets"); // [Agent][table]
|
|
if (!duk_has_prop_string(ctx, -1, key))
|
|
{
|
|
duk_push_array(ctx); // [Agent][table][Array]
|
|
duk_dup(ctx, -1); // [Agent][table][Array][Array]
|
|
duk_put_prop_string(ctx, -3, key); // [Agent][table][Array]
|
|
}
|
|
else
|
|
{
|
|
duk_get_prop_string(ctx, -1, key); // [Agent][table][Array]
|
|
}
|
|
duk_get_prop_string(ctx, -1, "push"); // [Agent][table][Array][push]
|
|
duk_swap_top(ctx, -2); // [Agent][table][push][this]
|
|
duk_dup(ctx, 0); // [AGent][table][push][this][socket]
|
|
|
|
duk_get_prop_string(ctx, -1, "setTimeout"); // [AGent][table][push][this][socket][setTimeout]
|
|
duk_dup(ctx, -2); // [AGent][table][push][this][socket][setTimeout][this]
|
|
duk_get_prop_string(ctx, -7, "keepAliveMsecs"); // [AGent][table][push][this][socket][setTimeout][this][milliseconds]
|
|
duk_push_c_function(ctx, ILibDuktape_HttpStream_Agent_keepSocketAlive_timeout, DUK_VARARGS); // [AGent][table][push][this][socket][setTimeout][this][milliseconds][callback]
|
|
duk_call_method(ctx, 2); duk_pop(ctx); // [AGent][table][push][this][socket]
|
|
duk_call_method(ctx, 1); // [Agent][table][retVal]
|
|
}
|
|
duk_push_int(ctx, retVal);
|
|
return(1);
|
|
}
|
|
void ILibDuktape_HttpStream_Agent_reuseSocketEx(duk_context *ctx, void ** args, int argsLen)
|
|
{
|
|
duk_push_this(ctx); // [immediate]
|
|
duk_del_prop_string(ctx, -1, "CR");
|
|
duk_del_prop_string(ctx, -1, "Socket");
|
|
duk_pop(ctx); // ...
|
|
|
|
duk_push_heapptr(ctx, args[1]); // [clientRequest]
|
|
duk_push_heapptr(ctx, args[0]); // [clientRequest][socket]
|
|
|
|
duk_get_prop_string(ctx, -1, "setTimeout"); // [clientRequest][socket][setTimeout]
|
|
duk_dup(ctx, -2); // [clientRequest][socket][setTimeout][this]
|
|
duk_push_int(ctx, 0); // [clientRequest][socket][setTimeout][this][0] (Disable Idle Timeout)
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // [clientRequest][socket]
|
|
|
|
ILibDuktape_CreateReadonlyProperty(ctx, "socket"); // [clientRequest]
|
|
|
|
duk_get_prop_string(ctx, -1, "emit"); // [clientRequest][emit]
|
|
duk_swap_top(ctx, -2); // [emit][this]
|
|
duk_push_string(ctx, "socket"); // [emit][this][name]
|
|
duk_push_heapptr(ctx, args[0]); // [emit][this][name][socket]
|
|
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "Agent.reuseSocket() => Error emitting 'socket' on clientRequest: %s", duk_safe_to_string(ctx, -1)); }
|
|
duk_pop(ctx); // ...
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_Agent_reuseSocket(duk_context *ctx)
|
|
{
|
|
// Yield to the next loop, before we emit a 'socket' event, because emitting this event before anyone has the clientRequest object is pointless
|
|
ILibDuktape_Immediate(ctx, (void*[]) { duk_get_heapptr(ctx, 0), duk_get_heapptr(ctx, 1) }, 2, ILibDuktape_HttpStream_Agent_reuseSocketEx);
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_Agent_createConnection_eventSink(duk_context *ctx)
|
|
{
|
|
// Error Sink
|
|
duk_push_this(ctx); // [socket]
|
|
char *key = Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_Socket2AgentKey, "");
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_Socket2Agent); // [socket][agent]
|
|
duk_get_prop_string(ctx, -1, "sockets"); // [socket][agent][socketsTable]
|
|
ILibDuktape_RemoveObjFromTable(ctx, -1, key, duk_get_heapptr(ctx, -3));
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_Agent_createConnection(duk_context *ctx)
|
|
{
|
|
int nargs = duk_get_top(ctx);
|
|
int i;
|
|
duk_size_t protocolLen;
|
|
char *protocol = Duktape_GetStringPropertyValueEx(ctx, 0, "protocol", "http:", &protocolLen);
|
|
|
|
duk_push_this(ctx); // [Agent]
|
|
if ((protocolLen == 6 && strncasecmp("https:", protocol, 6) == 0) || (protocolLen == 4 && strncasecmp("wss:", protocol, 4) == 0))
|
|
{
|
|
duk_eval_string(ctx, "require('tls');"); // [Agent][net]
|
|
duk_get_prop_string(ctx, -1, "connect"); // [Agent][net][createConnection]
|
|
}
|
|
else
|
|
{
|
|
duk_eval_string(ctx, "require('net');"); // [Agent][net]
|
|
duk_get_prop_string(ctx, -1, "createConnection"); // [Agent][net][createConnection]
|
|
}
|
|
duk_swap_top(ctx, -2); // [Agent][createConnection][this]
|
|
for (i = 0; i < nargs; ++i)
|
|
{
|
|
duk_dup(ctx, i);
|
|
}
|
|
duk_call_method(ctx, nargs); // [Agent][Socket]
|
|
duk_push_true(ctx); duk_put_prop_string(ctx, -2, ILibDuktape_Agent_SocketJustCreated);
|
|
duk_get_prop_string(ctx, -2, "getName"); // [Agent][Socket][getName]
|
|
duk_dup(ctx, -3); // [Agent][Socket][getName][this]
|
|
duk_dup(ctx, 0); // [Agent][Socket][getName][this][options]
|
|
duk_call_method(ctx, 1); // [Agent][Socket][key]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_Socket2AgentKey); // [Agent][Socket]
|
|
duk_dup(ctx, -2); // [Agent][Socket][Agent]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_Socket2Agent); // [Agent][Socket]
|
|
|
|
duk_get_prop_string(ctx, -2, "sockets"); // [Agent][Socket][socketsTable]
|
|
duk_get_prop_string(ctx, -2, ILibDuktape_Socket2AgentKey); // [Agent][Socket][socketsTable][key]
|
|
if (!duk_has_prop(ctx, -2)) // [Agent][Socket][socketsTable]
|
|
{
|
|
duk_get_prop_string(ctx, -2, ILibDuktape_Socket2AgentKey); // [Agent][Socket][socketsTable][key]
|
|
duk_push_array(ctx); // [Agent][Socket][socketsTable][key][array]
|
|
duk_put_prop(ctx, -3); // [Agent][Socket][socketsTable]
|
|
}
|
|
duk_get_prop_string(ctx, -2, ILibDuktape_Socket2AgentKey); // [Agent][Socket][socketsTable][key]
|
|
duk_get_prop(ctx, -2); // [Agent][Socket][socketsTable][array]
|
|
duk_get_prop_string(ctx, -1, "push"); // [Agent][Socket][socketsTable][array][push]
|
|
duk_swap_top(ctx, -2); // [Agent][Socket][socketsTable][push][this]
|
|
duk_dup(ctx, -4); // [Agent][Socket][socketsTable][push][this][socket]
|
|
duk_call_method(ctx, 1); duk_pop_2(ctx); // [Agent][Socket]
|
|
|
|
ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "error", ILibDuktape_HttpStream_Agent_createConnection_eventSink);
|
|
ILibDuktape_EventEmitter_AddOn_Infrastructure(ctx, -1, "close", ILibDuktape_HttpStream_Agent_socketEndSink);
|
|
|
|
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_HttpStream_Agent_new(duk_context *ctx)
|
|
{
|
|
if (duk_get_top(ctx) > 0 && duk_is_object(ctx, 0)) { duk_dup(ctx, 0); } else { duk_push_object(ctx); }
|
|
int keepAlive = Duktape_GetBooleanProperty(ctx, -1, "keepAlive", 1);
|
|
int keepAliveMsecs = Duktape_GetIntPropertyValue(ctx, -1, "keepAliveMsecs", 15000);
|
|
int maxSockets = Duktape_GetIntPropertyValue(ctx, -1, "maxSockets", 1);
|
|
int maxFreeSockets = Duktape_GetIntPropertyValue(ctx, -1, "maxFreeSockets", 32);
|
|
|
|
duk_push_object(ctx); // [Agent]
|
|
duk_push_this(ctx); // [Agent][http]
|
|
ILibDuktape_CreateReadonlyProperty(ctx, "http");
|
|
ILibDuktape_WriteID(ctx, "http.Agent");
|
|
duk_push_boolean(ctx, (duk_bool_t)keepAlive); // [Agent][keepAlive]
|
|
duk_put_prop_string(ctx, -2, "keepAlive"); // [Agent]
|
|
duk_push_int(ctx, keepAliveMsecs); // [Agent][keepAliveMsecs]
|
|
duk_put_prop_string(ctx, -2, "keepAliveMsecs"); // [Agent]
|
|
duk_push_int(ctx, maxSockets); // [Agent][maxSockets]
|
|
duk_put_prop_string(ctx, -2, "maxSockets"); // [Agent]
|
|
duk_push_int(ctx, maxFreeSockets); // [Agent][maxFreeSockets]
|
|
duk_put_prop_string(ctx, -2, "maxFreeSockets"); // [Agent]
|
|
|
|
duk_push_object(ctx); // [Agent][freeSockets]
|
|
duk_put_prop_string(ctx, -2, "freeSockets"); // [Agent]
|
|
duk_push_object(ctx); // [Agent][requests]
|
|
duk_put_prop_string(ctx, -2, "requests"); // [Agent]
|
|
duk_push_object(ctx); // [Agent][sockets]
|
|
duk_put_prop_string(ctx, -2, "sockets"); // [Agent]
|
|
|
|
ILibDuktape_CreateInstanceMethod(ctx, "getName", ILibDuktape_HttpStream_Agent_getName, 1);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "keepSocketAlive", ILibDuktape_HttpStream_Agent_keepSocketAlive, 1);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "reuseSocket", ILibDuktape_HttpStream_Agent_reuseSocket, 2);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "createConnection", ILibDuktape_HttpStream_Agent_createConnection, DUK_VARARGS);
|
|
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_httpStream_parseUri(duk_context *ctx)
|
|
{
|
|
duk_size_t uriLen;
|
|
char *uri;
|
|
if (!duk_is_string(ctx, 0)) { return(ILibDuktape_Error(ctx, "http.parseUri(): Invalid Parameters")); }
|
|
|
|
char *path, *addr;
|
|
unsigned short port;
|
|
int protocolIndex, i;
|
|
|
|
char *username = NULL;
|
|
char *password = NULL;
|
|
|
|
duk_string_split(ctx, 0, "@"); // [array]
|
|
if (duk_get_length(ctx, -1) == 2)
|
|
{
|
|
duk_array_shift(ctx, -1); // [array][http://user:pwd]
|
|
duk_string_split(ctx, -1, "://"); // [array][http://user:pwd][array]
|
|
duk_array_shift(ctx, -1); // [array][http://user:pwd][array][http]
|
|
duk_array_pop(ctx, -2); // [array][http://user:pwd][array][http][user:pwd]
|
|
duk_string_split(ctx, -1, ":"); // [array][http://user:pwd][array][http][user:pwd][array]
|
|
username = (char*)Duktape_GetStringPropertyIndexValue(ctx, -1, 0, NULL);
|
|
password = (char*)Duktape_GetStringPropertyIndexValue(ctx, -1, 1, NULL);
|
|
duk_string_split(ctx, 0, "@"); // [array][http://user:pwd][array][http][user:pwd][array][array]
|
|
duk_array_shift(ctx, -1); duk_pop(ctx); // [array][http://user:pwd][array][http][user:pwd][array][mydomain.com:xx/xx]
|
|
duk_push_string(ctx, "://"); // [array][http://user:pwd][array][http][user:pwd][array][mydomain.com:xx/xx][://]
|
|
duk_string_concat(ctx, -5); // [array][http://user:pwd][array][http][user:pwd][array][mydomain.com:xx/xx][http://]
|
|
duk_array_unshift(ctx, -2); // [array][http://user:pwd][array][http][user:pwd][array][http://mydomain.com:xx/xx]
|
|
duk_array_join(ctx, -1, "");
|
|
uri = (char*)duk_get_lstring(ctx, -1, &uriLen);
|
|
}
|
|
else
|
|
{
|
|
duk_string_split(ctx, 0, "://"); // [array]
|
|
if (duk_get_length(ctx, -1) == 1)
|
|
{
|
|
// Fix string format
|
|
duk_push_string(ctx, "http://"); // [array][http://]
|
|
duk_dup(ctx, 0); // [array][http://][string]
|
|
duk_string_concat(ctx, -2); // [array][uri]
|
|
uri = (char*)duk_get_lstring(ctx, -1, &uriLen);
|
|
}
|
|
else
|
|
{
|
|
uri = (char*)duk_get_lstring(ctx, 0, &uriLen);
|
|
}
|
|
}
|
|
|
|
protocolIndex = 1 + ILibString_IndexOf(uri, (int)uriLen, "://", 3);
|
|
if (protocolIndex > 0)
|
|
{
|
|
ILibParseUriEx(uri, (size_t)uriLen, &addr, &port, &path, NULL);
|
|
|
|
duk_push_object(ctx); // [options]
|
|
duk_push_lstring(ctx, uri, protocolIndex); // [options][protocol]
|
|
duk_put_prop_string(ctx, -2, "protocol");
|
|
duk_push_string(ctx, addr); // [options][host]
|
|
duk_put_prop_string(ctx, -2, "host");
|
|
duk_push_int(ctx, port); // [options][port]
|
|
duk_put_prop_string(ctx, -2, "port"); // [options]
|
|
duk_push_string(ctx, path); // [options][path]
|
|
duk_put_prop_string(ctx, -2, "path"); // [options]
|
|
duk_push_string(ctx, "GET"); // [options][method]
|
|
duk_put_prop_string(ctx, -2, "method"); // [options]
|
|
if (username != NULL) { duk_push_string(ctx, username); duk_put_prop_string(ctx, -2, "username"); }
|
|
if (password != NULL) { duk_push_string(ctx, password); duk_put_prop_string(ctx, -2, "password"); }
|
|
|
|
free(path);
|
|
free(addr);
|
|
}
|
|
else
|
|
{
|
|
duk_push_null(ctx);
|
|
}
|
|
password = NULL;
|
|
// [options]
|
|
uri = Duktape_GetStringPropertyValueEx(ctx, -1, "host", NULL, &uriLen);
|
|
if ((i = ILibString_IndexOf(uri, uriLen, "?", 1)) >= 0)
|
|
{
|
|
duk_push_sprintf(ctx, "/%s", uri + i); // [options][newpath]
|
|
duk_put_prop_string(ctx, -2, "path"); // [options]
|
|
duk_push_lstring(ctx, uri, i); // [options][newhost]
|
|
duk_put_prop_string(ctx, -2, "host"); // [options]
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
ILibTransport_DoneState ILibDuktape_httpStream_webSocket_WriteWebSocketPacket(ILibDuktape_WebSocket_State *state, int opcode, char *_buffer, int _bufferLen, ILibWebClient_WebSocket_FragmentFlags _bufferFragment)
|
|
{
|
|
char header[10];
|
|
int maskKeyInt;
|
|
int headerLen = 0;
|
|
unsigned short flags = state->noMasking == 0 ? WEBSOCKET_MASK : 0;
|
|
|
|
char *buffer;
|
|
int bufferLen;
|
|
ILibTransport_DoneState retVal = ILibTransport_DoneState_ERROR;
|
|
|
|
ILibWebClient_WebSocket_FragmentFlags bufferFragment;
|
|
size_t compressedLen = 0;
|
|
char *compressedBuffer = NULL;
|
|
|
|
if (!ILibMemory_CanaryOK(state)) { return(ILibTransport_DoneState_ERROR); }
|
|
|
|
buffer = _buffer;
|
|
bufferLen = _bufferLen;
|
|
bufferFragment = _bufferFragment;
|
|
|
|
if (bufferFragment == ILibWebClient_WebSocket_FragmentFlag_Complete)
|
|
{
|
|
if (state->WebSocketFragmentFlag_Write == 0)
|
|
{
|
|
// This is a self contained fragment
|
|
if (state->permessageDeflate != 0)
|
|
{
|
|
// Compression is enabled
|
|
if (state->minimumThreshold < bufferLen && state->skipCount == 0)
|
|
{
|
|
if (ILibDeflate(buffer, (size_t)bufferLen, NULL, &compressedLen, NULL) == 0)
|
|
{
|
|
if (compressedLen < (size_t)bufferLen)
|
|
{
|
|
compressedBuffer = (char*)ILibMemory_SmartAllocate(compressedLen);
|
|
if (ILibDeflate(buffer, (size_t)bufferLen, compressedBuffer, &compressedLen, NULL) == 0)
|
|
{
|
|
// Using Compresion
|
|
state->uncompressedSent += (uint64_t)bufferLen;
|
|
state->nonCompressibleCount = 0;
|
|
state->skipCount = 0;
|
|
buffer = compressedBuffer;
|
|
bufferLen = (int)compressedLen;
|
|
headerLen = ILibWebServer_WebSocket_CreateHeader(header, flags | WEBSOCKET_RSV1 | WEBSOCKET_FIN, (unsigned short)opcode, bufferLen);
|
|
state->uncompressedSent += (uint64_t)headerLen;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
state->nonCompressibleCount++;
|
|
if (state->nonCompressibleCount > state->minSkipCount)
|
|
{
|
|
state->skipCount = state->maxSkipCount;
|
|
state->nonCompressibleCount = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (state->minimumThreshold < bufferLen && state->skipCount > 0)
|
|
{
|
|
state->skipCount--;
|
|
}
|
|
}
|
|
|
|
if(headerLen == 0)
|
|
{
|
|
// No Compression
|
|
headerLen = ILibWebServer_WebSocket_CreateHeader(header, flags | WEBSOCKET_FIN, (unsigned short)opcode, bufferLen);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Termination of an ongoing Fragment
|
|
state->WebSocketFragmentFlag_Write = 0;
|
|
headerLen = ILibWebServer_WebSocket_CreateHeader(header, flags | WEBSOCKET_FIN, WEBSOCKET_OPCODE_FRAMECONT, bufferLen);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (state->WebSocketFragmentFlag_Write == 0)
|
|
{
|
|
// Start a new fragment
|
|
state->WebSocketFragmentFlag_Write = 1;
|
|
headerLen = ILibWebServer_WebSocket_CreateHeader(header, flags, (unsigned short)opcode, bufferLen);
|
|
}
|
|
else
|
|
{
|
|
// Continuation of an ongoing fragment
|
|
headerLen = ILibWebServer_WebSocket_CreateHeader(header, flags, WEBSOCKET_OPCODE_FRAMECONT, bufferLen);
|
|
}
|
|
}
|
|
|
|
if (flags & WEBSOCKET_MASK)
|
|
{
|
|
// We have to copy memory anyways to mask, so we might as well copy the extra few bytes and make a single buffer
|
|
char *dataFrame = ((headerLen + 4 + bufferLen) < 65535) ? ILibMemory_AllocateA(headerLen + 4 + bufferLen) : ILibMemory_SmartAllocate(headerLen + 4 + bufferLen);
|
|
char *maskKey = (dataFrame + headerLen);
|
|
memcpy_s(dataFrame, headerLen, header, headerLen);
|
|
|
|
// Mask the payload
|
|
util_random(4, maskKey);
|
|
maskKeyInt = ((int*)(maskKey))[0];
|
|
if (bufferLen > 0)
|
|
{
|
|
int x;
|
|
// Note that the line below will cause a SegFault on Linux when compiled with GCC -O3, the fault happens at the XOR (^) operation.
|
|
// Compiling with -O2 works also, doing the masking operating byte-by-byte also works with -O3.
|
|
for (x = 0; x < (bufferLen >> 2); ++x) { ((int*)(dataFrame+headerLen+4))[x] = ((int*)buffer)[x] ^ (int)maskKeyInt; } // Mask 4 bytes at a time (SegFaults with -O3).
|
|
for (x = (x << 2); x < bufferLen; ++x) { dataFrame[x + headerLen + 4] = buffer[x] ^ maskKey[x % 4]; } // Mask the reminder
|
|
}
|
|
retVal = ILibDuktape_DuplexStream_WriteData(state->encodedStream, dataFrame, headerLen + 4 + bufferLen) == 0 ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE;
|
|
ILibMemory_Free(dataFrame);
|
|
}
|
|
else
|
|
{
|
|
// Send payload without masking
|
|
if (ILibIsRunningOnChainThread(state->chain) != 0)
|
|
{
|
|
// We're on the Duktape Thread, so we can just call write multiple times, cuz we won't interleave with JavaScript
|
|
if (bufferLen > 0)
|
|
{
|
|
ILibDuktape_DuplexStream_WriteData(state->encodedStream, header, headerLen);
|
|
retVal = ILibDuktape_DuplexStream_WriteData(state->encodedStream, buffer, bufferLen) == 0 ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE;
|
|
}
|
|
else
|
|
{
|
|
retVal = ILibDuktape_DuplexStream_WriteData(state->encodedStream, header, headerLen) == 0 ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We're not on the Duktape Thread, so we need to merge these buffers, to make a single write
|
|
char *dataFrame = ILibMemory_SmartAllocate(headerLen + bufferLen);
|
|
memcpy_s(dataFrame, headerLen, header, headerLen);
|
|
memcpy_s(dataFrame + headerLen, bufferLen, buffer, bufferLen);
|
|
retVal = ILibDuktape_DuplexStream_WriteData(state->encodedStream, dataFrame, headerLen + bufferLen) == 0 ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE;
|
|
ILibMemory_Free(dataFrame);
|
|
}
|
|
}
|
|
state->actualSent += ((uint64_t)headerLen + (uint64_t)bufferLen);
|
|
if (compressedBuffer != NULL)
|
|
{
|
|
ILibMemory_Free(compressedBuffer);
|
|
}
|
|
else
|
|
{
|
|
state->uncompressedSent += ((uint64_t)bufferLen + (uint64_t)headerLen);
|
|
}
|
|
return retVal;
|
|
}
|
|
ILibTransport_DoneState ILibDuktape_httpStream_webSocket_EncodedWriteSink_DispatchUnshift(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen)
|
|
{
|
|
if (stream->writableStream->pipedReadable != NULL || stream->writableStream->pipedReadable_native != NULL)
|
|
{
|
|
if (stream->writableStream->pipedReadable != NULL)
|
|
{
|
|
duk_context *ctx = stream->writableStream->ctx;
|
|
if (ILibIsRunningOnChainThread(stream->readableStream->chain) != 0)
|
|
{
|
|
// We can dispatch directly
|
|
duk_push_external_buffer(ctx); // [ext]
|
|
duk_config_buffer(ctx, -1, buffer, (duk_size_t)bufferLen);
|
|
duk_push_heapptr(ctx, stream->writableStream->pipedReadable); // [ext][readable]
|
|
duk_get_prop_string(ctx, -1, "unshift"); // [ext][readable][unshift]
|
|
duk_swap_top(ctx, -2); // [ext][unshift][this]
|
|
duk_push_buffer_object(ctx, -3, 0, (duk_size_t)bufferLen, DUK_BUFOBJ_NODEJS_BUFFER);// [ext][unshift][this][buffer]
|
|
if (duk_pcall_method(ctx, 1) != 0)
|
|
{
|
|
ILibDuktape_Error(ctx, "http.webSocketStream.encoded.write() => Error calling unshift: %s", duk_safe_to_string(ctx, -1));
|
|
return(ILibTransport_DoneState_ERROR);
|
|
}
|
|
duk_pop_2(ctx); // ...
|
|
return(ILibTransport_DoneState_COMPLETE);
|
|
}
|
|
else
|
|
{
|
|
return(ILibTransport_DoneState_ERROR); // Something is wrong, if we're called by a JavaScript object on an external thread
|
|
}
|
|
}
|
|
|
|
// We were dispatched directly by a native ReadableStream
|
|
if (stream->writableStream->pipedReadable_native->UnshiftHandler == NULL ||
|
|
stream->writableStream->pipedReadable_native->UnshiftHandler(stream->writableStream->pipedReadable_native, bufferLen, stream->writableStream->pipedReadable_native->user) <= 0)
|
|
{
|
|
return(ILibTransport_DoneState_ERROR);
|
|
}
|
|
else
|
|
{
|
|
return(ILibTransport_DoneState_COMPLETE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We can't unshift, so we need to buffer. But we won't...
|
|
return(ILibTransport_DoneState_ERROR);
|
|
}
|
|
}
|
|
|
|
duk_ret_t ILibDuktape_WebSocket_DecompressData(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [self]
|
|
duk_push_array(ctx); // [self][array]
|
|
if (duk_has_prop_string(ctx, -2, "_ddata"))
|
|
{
|
|
duk_get_prop_string(ctx, -2, "_ddata"); // [self][array][buffer]
|
|
duk_array_push(ctx, -2); // [self][array]
|
|
}
|
|
duk_dup(ctx, 0); // [self][array][buffer]
|
|
duk_array_push(ctx, -2); // [self][array]
|
|
duk_buffer_concat(ctx); // [self][buffer]
|
|
duk_put_prop_string(ctx, -2, "_ddata"); // [self]
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_WebSocket_DecompressData_End(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [self]
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)Duktape_GetPointerProperty(ctx, -1, "state");
|
|
int reserved = Duktape_GetIntPropertyValue(ctx, -1, "reserved", 0);
|
|
duk_size_t bufLen;
|
|
char *buffer = Duktape_GetBufferPropertyEx(ctx, -1, "_ddata", &bufLen);
|
|
state->uncompressedReceived += (uint64_t)bufLen;
|
|
ILibDuktape_DuplexStream_WriteDataEx(state->decodedStream, reserved, buffer, (int)bufLen);
|
|
return(0);
|
|
}
|
|
ILibTransport_DoneState ILibDuktape_httpStream_webSocket_EncodedWriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user)
|
|
{
|
|
int x;
|
|
int i = 2;
|
|
int plen;
|
|
unsigned short hdr;
|
|
char* maskingKey = NULL;
|
|
int FIN;
|
|
unsigned char OPCODE;
|
|
unsigned char RSV;
|
|
unsigned char RSV1;
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user;
|
|
|
|
if (!ILibMemory_CanaryOK(state)) { return(ILibTransport_DoneState_ERROR); }
|
|
|
|
if (bufferLen < 2)
|
|
{
|
|
// We need at least 2 bytes to read enough of the headers to know how long the frame is
|
|
return(ILibDuktape_httpStream_webSocket_EncodedWriteSink_DispatchUnshift(stream, buffer, bufferLen));
|
|
}
|
|
|
|
hdr = ntohs(((unsigned short*)(buffer))[0]);
|
|
FIN = (hdr & WEBSOCKET_FIN) != 0;
|
|
OPCODE = (hdr & WEBSOCKET_OPCODE) >> 8;
|
|
RSV = (hdr & WEBSOCKET_RSV) >> 8;
|
|
RSV1 = (hdr & WEBSOCKET_RSV1) >> 8;
|
|
|
|
if (state->permessageDeflate == 0 && RSV != 0)
|
|
{
|
|
char msg[] = "Reserved Field of Websocket was not ZERO";
|
|
Duktape_Console_Log(state->ctx, state->chain, ILibDuktape_LogType_Error, msg, sizeof(msg) - 1);
|
|
return(ILibTransport_DoneState_ERROR);
|
|
}
|
|
|
|
plen = (unsigned char)(hdr & WEBSOCKET_PLEN);
|
|
if (plen == 126)
|
|
{
|
|
if (bufferLen < 4) { return(ILibDuktape_httpStream_webSocket_EncodedWriteSink_DispatchUnshift(stream, buffer, bufferLen)); } // We need at least 4 bytes to read enough of the headers
|
|
plen = (unsigned short)ntohs(((unsigned short*)(buffer))[1]);
|
|
i += 2;
|
|
}
|
|
else if (plen == 127)
|
|
{
|
|
if (bufferLen < 10)
|
|
{
|
|
return(ILibDuktape_httpStream_webSocket_EncodedWriteSink_DispatchUnshift(stream, buffer, bufferLen)); // We need at least 10 bytes to read enough of the headers
|
|
}
|
|
else
|
|
{
|
|
unsigned long long v = ILibNTOHLL(((unsigned long long*)(buffer + 2))[0]);
|
|
if (v > 0x7FFFFFFFUL)
|
|
{
|
|
// this value is too big to store in a 32 bit signed variable, so disconnect the websocket.
|
|
return(ILibTransport_DoneState_ERROR);
|
|
}
|
|
else
|
|
{
|
|
// this value can be represented with a signed 32 bit variable
|
|
plen = (int)v;
|
|
i += 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bufferLen < (i + plen + ((unsigned char)(hdr & WEBSOCKET_MASK) != 0 ? 4 : 0)))
|
|
{
|
|
return(ILibDuktape_httpStream_webSocket_EncodedWriteSink_DispatchUnshift(stream, buffer, bufferLen)); // Don't have the entire packet
|
|
}
|
|
|
|
maskingKey = ((unsigned char)(hdr & WEBSOCKET_MASK) == 0) ? NULL : (buffer + i);
|
|
if (maskingKey != NULL)
|
|
{
|
|
// Unmask the data
|
|
i += 4; // Move ptr to start of data
|
|
|
|
int maskKeyInt = ((int*)(maskingKey))[0];
|
|
if (plen > 0)
|
|
{
|
|
for (x = 0; x < (plen >> 2); ++x) { ((int*)(buffer+i))[x] = ((int*)(buffer+i))[x] ^ (int)maskKeyInt; } // Mask 4 bytes at a time
|
|
for (x = (x << 2); x < plen; ++x) { buffer[x + i] = buffer[x + i] ^ maskingKey[x % 4]; } // Mask the reminder
|
|
}
|
|
}
|
|
|
|
if (OPCODE < 0x8)
|
|
{
|
|
// NON-CONTROL OP-CODE
|
|
// We will try to automatically re-assemble fragments, up to the max buffer size the user specified
|
|
if (OPCODE != 0) { state->WebSocketDataFrameType = (int)OPCODE; } // Set the DataFrame Type, so the user can query it
|
|
if (FIN != 0 && state->WebSocketFragmentIndex == 0) { state->actualReceived += (uint64_t)plen; }
|
|
|
|
if (state->permessageDeflate != 0 && RSV1 != 0)
|
|
{
|
|
// This is compressed
|
|
if (state->WebSocketFragmentIndex == 0)
|
|
{
|
|
duk_context *ctx = state->encodedStream->writableStream->ctx;
|
|
int ctx_top = duk_get_top(ctx);
|
|
|
|
duk_push_heapptr(ctx, state->encodedStream->writableStream->obj); // [stream]
|
|
if (duk_has_prop_string(ctx, -1, ILibDuktape_WebSocket_Decompressor))
|
|
{
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WebSocket_Decompressor); // [stream][decompresor]
|
|
}
|
|
else
|
|
{
|
|
duk_eval_string(ctx, "require('compressed-stream');"); // [stream][CS]
|
|
duk_prepare_method_call(ctx, -1, "createDecompressor"); // [stream][CS][createDecompressor][this]
|
|
duk_remove(ctx, -3); // [stream][createDecompressor][this]
|
|
duk_push_object(ctx); // [stream][createDecompressor][this][options]
|
|
duk_push_int(ctx, -15); duk_put_prop_string(ctx, -2, "WBITS"); // [stream][createDecompressor][this][options]
|
|
if (duk_pcall_method(ctx, 1) != 0) // [stream][decompressor]
|
|
{
|
|
ILibDuktape_Process_UncaughtExceptionEx(ctx, "websocket inflate() error ");
|
|
duk_set_top(ctx, ctx_top);
|
|
return(ILibTransport_DoneState_ERROR);
|
|
}
|
|
duk_dup(ctx, -1); // [stream][decompressor][dup]
|
|
duk_put_prop_string(ctx, -3, ILibDuktape_WebSocket_Decompressor); // [stream][decompressor]
|
|
duk_push_pointer(ctx, state); duk_put_prop_string(ctx, -2, "state");
|
|
duk_push_int(ctx, OPCODE == WEBSOCKET_OPCODE_TEXTFRAME ? 1 : 0); duk_put_prop_string(ctx, -2, "reserved");
|
|
ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "data", ILibDuktape_WebSocket_DecompressData);
|
|
ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "end", ILibDuktape_WebSocket_DecompressData_End);
|
|
}
|
|
duk_prepare_method_call(ctx, -1, FIN != 0 ? "end" : "write"); // [stream][decompressor][write][this]
|
|
duk_push_external_buffer(ctx); // [stream][decompressor][write][this][buffer]
|
|
duk_config_buffer(ctx, -1, buffer + i, (duk_size_t)plen);
|
|
duk_push_buffer_object(ctx, -1, 0, (duk_size_t)plen, DUK_BUFOBJ_NODEJS_BUFFER); //.[decompressor][write][this][buffer][njsbuffer]
|
|
duk_remove(ctx, -2); // [stream][decompressor][write][this][buffer]
|
|
duk_pcall_method(ctx, 1); // [stream][decompressor][ret]
|
|
if (FIN != 0)
|
|
{
|
|
duk_del_prop_string(ctx, -3, ILibDuktape_WebSocket_Decompressor);
|
|
}
|
|
duk_set_top(ctx, ctx_top);
|
|
|
|
if (bufferLen > (i + plen))
|
|
{
|
|
return(ILibDuktape_httpStream_webSocket_EncodedWriteSink_DispatchUnshift(stream, buffer + i + plen, bufferLen - (i + plen))); // We need at least 10 bytes to read enough of the headers
|
|
}
|
|
|
|
return(ILibTransport_DoneState_COMPLETE);
|
|
}
|
|
}
|
|
|
|
|
|
if (FIN != 0 && state->WebSocketFragmentIndex == 0)
|
|
{
|
|
// We have an entire fragment, and we didn't save any of it yet... We can just forward it up without copying the buffer
|
|
ILibDuktape_DuplexStream_WriteDataEx(state->decodedStream, OPCODE == WEBSOCKET_OPCODE_TEXTFRAME ? 1 : 0, buffer + i, plen);
|
|
}
|
|
else
|
|
{
|
|
if (state->WebSocketFragmentIndex + plen >= state->WebSocketFragmentBufferSize)
|
|
{
|
|
// Need to grow the buffer
|
|
if (state->WebSocketFragmentBufferSize == 0) { state->WebSocketFragmentBufferSize = 4096; }
|
|
state->WebSocketFragmentBufferSize = state->WebSocketFragmentBufferSize * 2;
|
|
if ((state->WebSocketFragmentBuffer = (char*)realloc(state->WebSocketFragmentBuffer, state->WebSocketFragmentBufferSize)) == NULL) { ILIBCRITICALEXIT(254); } // MS Static Analyser erroneously reports that this leaks the original memory block
|
|
}
|
|
|
|
memcpy_s(state->WebSocketFragmentBuffer + state->WebSocketFragmentIndex, state->WebSocketFragmentBufferSize - state->WebSocketFragmentIndex, buffer + i, plen);
|
|
state->WebSocketFragmentIndex += plen;
|
|
|
|
if (FIN != 0)
|
|
{
|
|
ILibDuktape_DuplexStream_WriteDataEx(state->decodedStream, OPCODE == WEBSOCKET_OPCODE_TEXTFRAME ? 1 : 0, state->WebSocketFragmentBuffer, state->WebSocketFragmentIndex);
|
|
state->WebSocketFragmentIndex = 0; // Reset (We can write to the start of the buffer)
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// CONTROL
|
|
switch (OPCODE)
|
|
{
|
|
case WEBSOCKET_OPCODE_CLOSE:
|
|
state->closed = 1;
|
|
ILibDuktape_DuplexStream_WriteEnd(state->decodedStream);
|
|
if (ILibMemory_CanaryOK(state) && ILibIsRunningOnChainThread(state->chain) != 0 && state->encodedStream->writableStream->pipedReadable != NULL)
|
|
{
|
|
duk_context *ctx = state->ctx;
|
|
duk_push_heapptr(state->ctx, state->encodedStream->writableStream->pipedReadable); // [stream]
|
|
duk_get_prop_string(state->ctx, -1, "end"); // [stream][end]
|
|
duk_swap_top(state->ctx, -2); // [end][this]
|
|
if (duk_pcall_method(state->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.webSocketStream.write(): Error Dispatching 'end' "); }
|
|
duk_pop(ctx); // ...
|
|
}
|
|
break;
|
|
case WEBSOCKET_OPCODE_PING:
|
|
if (ILibIsRunningOnChainThread(state->chain) != 0)
|
|
{
|
|
duk_push_heapptr(state->ctx, state->decodedStream->ParentObject); // [stream]
|
|
duk_get_prop_string(state->ctx, -1, "emit"); // [stream][emit]
|
|
duk_swap_top(state->ctx, -2); // [emit][this]
|
|
duk_push_string(state->ctx, "ping"); // [emit][this][ping]
|
|
|
|
if (duk_pcall_method(state->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(state->ctx, "http.webSocketStream.write(): Error Dispatching Ping "); }
|
|
duk_pop(state->ctx); // ...
|
|
}
|
|
|
|
ILibDuktape_httpStream_webSocket_WriteWebSocketPacket(state, WEBSOCKET_OPCODE_PONG, NULL, 0, ILibWebClient_WebSocket_FragmentFlag_Complete);
|
|
break;
|
|
case WEBSOCKET_OPCODE_PONG:
|
|
if (ILibIsRunningOnChainThread(state->chain) != 0)
|
|
{
|
|
duk_push_heapptr(state->ctx, state->decodedStream->ParentObject); // [stream]
|
|
duk_get_prop_string(state->ctx, -1, "emit"); // [stream][emit]
|
|
duk_swap_top(state->ctx, -2); // [emit][this]
|
|
duk_push_string(state->ctx, "pong"); // [emit][this][pong]
|
|
|
|
if (duk_pcall_method(state->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(state->ctx, "http.webSocketStream.write(): Error Dispatching Pong "); }
|
|
duk_pop(state->ctx); // ...
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bufferLen > (i + plen))
|
|
{
|
|
return(ILibDuktape_httpStream_webSocket_EncodedWriteSink_DispatchUnshift(stream, buffer + i + plen, bufferLen - (i + plen))); // We need at least 10 bytes to read enough of the headers
|
|
}
|
|
|
|
return(ILibTransport_DoneState_COMPLETE);
|
|
}
|
|
void ILibDuktape_httpStream_webSocket_EncodedEndSink(ILibDuktape_DuplexStream *stream, void *user)
|
|
{
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user;
|
|
if (!ILibMemory_CanaryOK(state) || duk_ctx_shutting_down(state->ctx)) { return; }
|
|
|
|
duk_push_heapptr(state->ctx, state->ObjectPtr); // [websocket]
|
|
duk_get_prop_string(state->ctx, -1, "decoded"); // [websocket][decoded]
|
|
duk_del_prop_string(state->ctx, -1, ILibDuktape_WSDEC2WS);
|
|
duk_prepare_method_call(state->ctx, -1, "unpipe"); // [websocket][decoded][pmc][this]
|
|
duk_pcall_method(state->ctx, 0); duk_pop(state->ctx); // [websocket][decoded]
|
|
|
|
ILibDuktape_DeleteReadOnlyProperty(state->ctx, -1, "decoded");
|
|
ILibDuktape_DeleteReadOnlyProperty(state->ctx, -1, "encoded");
|
|
duk_pop(state->ctx); // ...
|
|
|
|
duk_push_this(state->ctx); // [encoded]
|
|
duk_del_prop_string(state->ctx, -1, ILibDuktape_WSENC2WS);
|
|
duk_pop(state->ctx); // ...
|
|
|
|
if (!state->closed) { ILibDuktape_DuplexStream_WriteEnd(state->decodedStream); }
|
|
}
|
|
void ILibDuktape_httpStream_webSocket_EncodedPauseSink_Chain(void *chain, void *user)
|
|
{
|
|
if (!ILibMemory_CanaryOK(user)) { return; }
|
|
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user;
|
|
duk_context *ctx = state->decodedStream->writableStream->ctx;
|
|
|
|
if (state->decodedStream->writableStream->pipedReadable != NULL)
|
|
{
|
|
duk_push_heapptr(ctx, state->decodedStream->writableStream->pipedReadable); // [readable]
|
|
duk_get_prop_string(ctx, -1, "pause"); // [readable][pause]
|
|
duk_swap_top(ctx, -2); // [pause][this]
|
|
if (duk_pcall_method(ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.webSocketStream.Encoded_Pause(): Error pausing upstream "); }
|
|
duk_pop(ctx); // ...
|
|
}
|
|
}
|
|
void ILibDuktape_httpStream_webSocket_EncodedPauseSink(ILibDuktape_DuplexStream *sender, void *user)
|
|
{
|
|
//printf("WebSocket.Encoded.Pause();\n");
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user;
|
|
if (state->decodedStream->writableStream->pipedReadable_native != NULL && state->decodedStream->writableStream->pipedReadable_native->PauseHandler != NULL)
|
|
{
|
|
state->decodedStream->writableStream->pipedReadable_native->paused = 1;
|
|
state->decodedStream->writableStream->pipedReadable_native->PauseHandler(state->decodedStream->writableStream->pipedReadable_native, state->decodedStream->writableStream->pipedReadable_native->user);
|
|
}
|
|
else
|
|
{
|
|
if (ILibIsRunningOnChainThread(state->chain))
|
|
{
|
|
ILibDuktape_httpStream_webSocket_EncodedPauseSink_Chain(NULL, state);
|
|
}
|
|
else
|
|
{
|
|
Duktape_RunOnEventLoop(state->chain, duk_ctx_nonce(state->ctx), state->ctx, ILibDuktape_httpStream_webSocket_EncodedPauseSink_Chain, NULL, state);
|
|
}
|
|
}
|
|
}
|
|
void ILibDuktape_httpStream_webSocket_EncodedResumeSink_Chain(void *chain, void *user)
|
|
{
|
|
if (!ILibMemory_CanaryOK(user)) { return; }
|
|
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user;
|
|
duk_context *ctx = state->decodedStream->writableStream->ctx;
|
|
|
|
if (state->decodedStream->writableStream->pipedReadable == NULL) { return; }
|
|
duk_push_heapptr(ctx, state->decodedStream->writableStream->pipedReadable); // [readable]
|
|
duk_get_prop_string(ctx, -1, "resume"); // [readable][resume]
|
|
duk_swap_top(ctx, -2); // [resume][this]
|
|
if (duk_pcall_method(ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.webSocketStream.Encoded_Resume(): Error resuming upstream "); }
|
|
duk_pop(ctx); // ...
|
|
}
|
|
void ILibDuktape_httpStream_webSocket_EncodedResumeSink(ILibDuktape_DuplexStream *sender, void *user)
|
|
{
|
|
//printf("WebSocket.Encoded.Resume();\n");
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user;
|
|
if (state->decodedStream->writableStream->pipedReadable_native != NULL && state->decodedStream->writableStream->pipedReadable_native->ResumeHandler != NULL)
|
|
{
|
|
state->decodedStream->writableStream->pipedReadable_native->paused = 0;
|
|
state->decodedStream->writableStream->pipedReadable_native->ResumeHandler(state->decodedStream->writableStream->pipedReadable_native, state->decodedStream->writableStream->pipedReadable_native->user);
|
|
}
|
|
else
|
|
{
|
|
if (ILibIsRunningOnChainThread(state->chain))
|
|
{
|
|
ILibDuktape_httpStream_webSocket_EncodedResumeSink_Chain(NULL, state);
|
|
}
|
|
else
|
|
{
|
|
Duktape_RunOnEventLoop(state->chain, duk_ctx_nonce(state->ctx), state->ctx, ILibDuktape_httpStream_webSocket_EncodedResumeSink_Chain, NULL, state);
|
|
}
|
|
}
|
|
}
|
|
int ILibDuktape_httpStream_webSocket_EncodedUnshiftSink(ILibDuktape_DuplexStream *sender, int unshiftBytes, void *user)
|
|
{
|
|
// Block Mode should never need to be unshifted
|
|
return(0);
|
|
}
|
|
|
|
ILibTransport_DoneState ILibDuktape_httpStream_webSocket_DecodedWriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user)
|
|
{
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user;
|
|
return(ILibDuktape_httpStream_webSocket_WriteWebSocketPacket(state, stream->writableStream->Reserved == 1 ? ILibWebClient_WebSocket_DataType_TEXT : ILibWebClient_WebSocket_DataType_BINARY, buffer, bufferLen, ILibWebClient_WebSocket_FragmentFlag_Complete));
|
|
}
|
|
void ILibDuktape_httpStream_webSocket_DecodedEndSink(ILibDuktape_DuplexStream *stream, void *user)
|
|
{
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user;
|
|
ILibDuktape_httpStream_webSocket_WriteWebSocketPacket(state, WEBSOCKET_OPCODE_CLOSE, NULL, 0, ILibWebClient_WebSocket_FragmentFlag_Complete);
|
|
}
|
|
void ILibDuktape_httpStream_webSocket_DecodedPauseSink_Chain(void *chain, void *user)
|
|
{
|
|
if (!ILibMemory_CanaryOK(user)) { return; }
|
|
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user;
|
|
duk_context *ctx = state->encodedStream->writableStream->ctx;
|
|
|
|
if (state->encodedStream->writableStream->pipedReadable == NULL)
|
|
{
|
|
// We're not piped yet, so just set a flag, and we'll make sure we don't resume
|
|
state->noResume = 1;
|
|
return;
|
|
}
|
|
|
|
|
|
duk_push_heapptr(ctx, state->encodedStream->writableStream->pipedReadable); // [readable]
|
|
duk_get_prop_string(ctx, -1, "pause"); // [readable][pause]
|
|
duk_swap_top(ctx, -2); // [pause][this]
|
|
if (duk_pcall_method(ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.webSocketStream.Decoded_Pause(): Error pausing upstream "); }
|
|
duk_pop(ctx); // ...
|
|
}
|
|
void ILibDuktape_httpStream_webSocket_DecodedPauseSink(ILibDuktape_DuplexStream *sender, void *user)
|
|
{
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user;
|
|
if (state == NULL || !ILibMemory_CanaryOK(state) || state->encodedStream == NULL || state->encodedStream->writableStream == NULL) { return; } // ERROR
|
|
|
|
if (state->encodedStream->writableStream->pipedReadable_native != NULL && state->encodedStream->writableStream->pipedReadable_native->PauseHandler != NULL)
|
|
{
|
|
state->encodedStream->writableStream->pipedReadable_native->paused = 1;
|
|
state->encodedStream->writableStream->pipedReadable_native->PauseHandler(state->encodedStream->writableStream->pipedReadable_native, state->encodedStream->writableStream->pipedReadable_native->user);
|
|
}
|
|
else
|
|
{
|
|
if (ILibIsRunningOnChainThread(state->chain))
|
|
{
|
|
ILibDuktape_httpStream_webSocket_DecodedPauseSink_Chain(NULL, state);
|
|
}
|
|
else
|
|
{
|
|
Duktape_RunOnEventLoop(state->chain, duk_ctx_nonce(state->ctx), state->ctx, ILibDuktape_httpStream_webSocket_DecodedPauseSink_Chain, NULL, state);
|
|
}
|
|
}
|
|
}
|
|
void ILibDuktape_httpStream_webSocket_DecodedResumeSink_Chain(void *chain, void *user)
|
|
{
|
|
if (!ILibMemory_CanaryOK(user)) { return; }
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user;
|
|
duk_context *ctx = state->encodedStream->writableStream->ctx;
|
|
|
|
if (state->encodedStream->writableStream->pipedReadable == NULL)
|
|
{
|
|
state->noResume = 0;
|
|
return;
|
|
}
|
|
|
|
duk_push_heapptr(ctx, state->encodedStream->writableStream->pipedReadable); // [readable]
|
|
duk_get_prop_string(ctx, -1, "resume"); // [readable][resume]
|
|
duk_swap_top(ctx, -2); // [resume][this]
|
|
if (duk_pcall_method(ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.webSocketStream.Decoded_Resume(): Error resuming upstream "); }
|
|
duk_pop(ctx); // ...
|
|
}
|
|
void ILibDuktape_httpStream_webSocket_DecodedResumeSink(ILibDuktape_DuplexStream *sender, void *user)
|
|
{
|
|
if (!ILibMemory_CanaryOK(user)) { return; }
|
|
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user;
|
|
if (state->encodedStream->writableStream->pipedReadable_native != NULL && state->encodedStream->writableStream->pipedReadable_native->ResumeHandler != NULL)
|
|
{
|
|
state->encodedStream->writableStream->pipedReadable_native->paused = 0;
|
|
state->encodedStream->writableStream->pipedReadable_native->ResumeHandler(state->encodedStream->writableStream->pipedReadable_native, state->encodedStream->writableStream->pipedReadable_native->user);
|
|
}
|
|
else
|
|
{
|
|
if (ILibIsRunningOnChainThread(state->chain))
|
|
{
|
|
ILibDuktape_httpStream_webSocket_DecodedResumeSink_Chain(NULL, state);
|
|
}
|
|
else
|
|
{
|
|
Duktape_RunOnEventLoop(state->chain, duk_ctx_nonce(state->ctx), state->ctx, ILibDuktape_httpStream_webSocket_DecodedResumeSink_Chain, NULL, state);
|
|
}
|
|
}
|
|
}
|
|
int ILibDuktape_httpStream_webSocket_DecodedUnshiftSink(ILibDuktape_DuplexStream *sender, int unshiftBytes, void *user)
|
|
{
|
|
// Block Mode Data Transfers should never need to be unshifted, otherwise you're doing it wrong
|
|
return(0);
|
|
}
|
|
|
|
duk_ret_t ILibDuktape_httpStream_webSocketStream_finalizer(duk_context *ctx)
|
|
{
|
|
duk_get_prop_string(ctx, 0, ILibDuktape_WebSocket_StatePtr);
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)Duktape_GetBuffer(ctx, -1, NULL);
|
|
|
|
if (state->encodedStream != NULL && state->encodedStream->writableStream->pipedReadable != NULL)
|
|
{
|
|
duk_push_heapptr(ctx, state->encodedStream->writableStream->pipedReadable); // [readable]
|
|
duk_get_prop_string(ctx, -1, "unpipe"); // [readable][unpipe]
|
|
duk_swap_top(ctx, -2); // [unpipe][this]
|
|
duk_push_heapptr(ctx, state->encodedStream->writableStream->obj); // [unpipe][this][ws]
|
|
duk_call_method(ctx, 1); duk_pop(ctx); // ...
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_httpStream_webSocketStream_sendPing(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [WS_DEC]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WSDEC2WS); // [WS_DEC][WS]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WebSocket_StatePtr); // [WS_DEC][WS][PTR]
|
|
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)Duktape_GetBuffer(ctx, -1, NULL);
|
|
ILibDuktape_httpStream_webSocket_WriteWebSocketPacket(state, WEBSOCKET_OPCODE_PING, NULL, 0, ILibWebClient_WebSocket_FragmentFlag_Complete);
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_httpStream_webSocketStream_sendPong(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx); // [WS_DEC]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WSDEC2WS); // [WS_DEC][WS]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WebSocket_StatePtr); // [WS_DEC][WS][PTR]
|
|
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)Duktape_GetBuffer(ctx, -1, NULL);
|
|
ILibDuktape_httpStream_webSocket_WriteWebSocketPacket(state, WEBSOCKET_OPCODE_PONG, NULL, 0, ILibWebClient_WebSocket_FragmentFlag_Complete);
|
|
return(0);
|
|
}
|
|
|
|
duk_ret_t ILibDuktape_httpStream_webSocketStream_encodedPiped(duk_context *ctx)
|
|
{
|
|
// Someone Piped to the Encoded Stream
|
|
duk_push_this(ctx); // [ENC]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WSENC2WS); // [ENC][WS]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WebSocket_StatePtr); // [ENC][WS][state]
|
|
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)Duktape_GetBuffer(ctx, -1, NULL);
|
|
if (state->noResume)
|
|
{
|
|
state->noResume = 0;
|
|
duk_push_heapptr(state->ctx, state->encodedStream->writableStream->pipedReadable); // [Readable]
|
|
duk_get_prop_string(state->ctx, -1, "pause"); // [Readable][pause]
|
|
duk_swap_top(ctx, -2); // [pause][this]
|
|
duk_call_method(ctx, 0);
|
|
}
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_httpStream_webSocketStream_encoded_Finalizer(duk_context *ctx)
|
|
{
|
|
if (duk_has_prop_string(ctx, -1, ILibDuktape_WSENC2WS))
|
|
{
|
|
duk_get_prop_string(ctx, 0, ILibDuktape_WSENC2WS);
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WebSocket_StatePtr);
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)Duktape_GetBuffer(ctx, -1, NULL);
|
|
|
|
state->encodedStream = NULL;
|
|
}
|
|
|
|
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_httpStream_webSocketStream_decoded_Finalizer(duk_context *ctx)
|
|
{
|
|
return(0);
|
|
}
|
|
#ifdef _SSL_KEYS_EXPORTABLE
|
|
duk_ret_t ILibDuktape_httpStream_webSocket_exportKeys(duk_context *ctx)
|
|
{
|
|
duk_push_this(ctx);
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WSDEC2WS);
|
|
|
|
ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_WebSocket_StatePtr);
|
|
if (state->encodedStream->writableStream->pipedReadable != NULL)
|
|
{
|
|
duk_push_heapptr(ctx, state->encodedStream->writableStream->pipedReadable); // [readable]
|
|
duk_get_prop_string(ctx, -1, "_exportKeys"); // [readable][exportKeys]
|
|
duk_swap_top(ctx, -2); // [exportKeys][this]
|
|
duk_call_method(ctx, 0); // [ret]
|
|
return(1);
|
|
}
|
|
return(ILibDuktape_Error(ctx, "Error exporting keys"));
|
|
}
|
|
#endif
|
|
void ILibDuktape_httpStream_webSocketStream_descriptorMetadataEx(duk_context *ctx, void ** args, int argsLen)
|
|
{
|
|
ILibDuktape_WebSocket_State *ws = (ILibDuktape_WebSocket_State*)args[0];
|
|
char *str = (char*)args[1];
|
|
|
|
if(ILibMemory_CanaryOK(ws) && ws->encodedStream->writableStream->pipedReadable != NULL)
|
|
{
|
|
duk_idx_t top = duk_get_top(ctx);
|
|
duk_push_heapptr(ctx, ws->encodedStream->writableStream->pipedReadable); // [WebSocket_Decoded][WebSocket][Readable]
|
|
char * tmp = (char*)duk_push_sprintf(ctx, "%s, %s", ILibChain_Link_GetMetadata(Duktape_GetPointerProperty(ctx, -1, ILibDuktape_ChainLinkPtr)), str);
|
|
char *tmp2 = ILibMemory_SmartAllocate(duk_get_length(ctx, -1) + 1); // [WebSocket_Decoded][WebSocket][Readable][str]
|
|
memcpy_s(tmp2, ILibMemory_Size(tmp2), tmp, ILibMemory_Size(tmp2) - 1);
|
|
ILibChain_Link_SetMetadata(Duktape_GetPointerProperty(ctx, -2, ILibDuktape_ChainLinkPtr), tmp2);
|
|
duk_set_top(ctx, top);
|
|
}
|
|
}
|
|
|
|
duk_ret_t ILibDuktape_httpStream_webSocketStream_descriptorMetadata(duk_context *ctx)
|
|
{
|
|
ILibDuktape_WebSocket_State *ws = NULL;
|
|
duk_push_this(ctx); // [WebSocket_Decoded]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WSDEC2WS); // [WebSocket_Decoded][WebSocket]
|
|
ws = (ILibDuktape_WebSocket_State*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_WebSocket_StatePtr);
|
|
if (ws != NULL)
|
|
{
|
|
if (ws->encodedStream->writableStream->pipedReadable == NULL)
|
|
{
|
|
ILibDuktape_Immediate(ctx, (void *[]) { ws, (void*)duk_require_string(ctx, 0) }, 2, ILibDuktape_httpStream_webSocketStream_descriptorMetadataEx);
|
|
}
|
|
else
|
|
{
|
|
ILibDuktape_httpStream_webSocketStream_descriptorMetadataEx(ctx, (void *[]) { ws, (void*)duk_require_string(ctx, 0) }, 2);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
duk_ret_t ILibDuktape_WebSocket_bytesReceived_uncompressed(duk_context *ctx)
|
|
{
|
|
int16_t test = 0x0001;
|
|
int LE = ((char*)&test)[0] ? 1 : 0;
|
|
|
|
ILibDuktape_WebSocket_State *ws = NULL;
|
|
duk_push_this(ctx); // [WebSocket_Decoded]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WSDEC2WS); // [WebSocket_Decoded][WebSocket]
|
|
ws = (ILibDuktape_WebSocket_State*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_WebSocket_StatePtr);
|
|
|
|
uint64_t v = ws->uncompressedReceived;
|
|
duk_eval_string(ctx, "require('bignum')"); // [var][bignum]
|
|
duk_prepare_method_call(ctx, -1, "fromBuffer"); // [var][bignum][fromBuffer][this]
|
|
duk_push_external_buffer(ctx); // [var][bignum][fromBuffer][this][buffer]
|
|
duk_config_buffer(ctx, -1, &v, sizeof(v));
|
|
duk_push_buffer_object(ctx, -1, 0, sizeof(v), DUK_BUFOBJ_NODEJS_BUFFER);// [var][bignum][fromBuffer][this][buffer][nodebuffer]
|
|
duk_remove(ctx, -2); // [var][bignum][fromBuffer][this][nodeBuffer]
|
|
duk_push_object(ctx); // [var][bignum][fromBuffer][this][nodeBuffer][options
|
|
duk_push_string(ctx, LE ? "little" : "big"); // [var][bignum][fromBuffer][this][nodeBuffer][options][endian]
|
|
duk_put_prop_string(ctx, -2, "endian");
|
|
duk_call_method(ctx, 2); // [var][bignum][bignum]
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_WebSocket_bytesReceived_actual(duk_context *ctx)
|
|
{
|
|
int16_t test = 0x0001;
|
|
int LE = ((char*)&test)[0] ? 1 : 0;
|
|
|
|
ILibDuktape_WebSocket_State *ws = NULL;
|
|
duk_push_this(ctx); // [WebSocket_Decoded]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WSDEC2WS); // [WebSocket_Decoded][WebSocket]
|
|
ws = (ILibDuktape_WebSocket_State*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_WebSocket_StatePtr);
|
|
|
|
uint64_t v = ws->actualReceived;
|
|
duk_eval_string(ctx, "require('bignum')"); // [var][bignum]
|
|
duk_prepare_method_call(ctx, -1, "fromBuffer"); // [var][bignum][fromBuffer][this]
|
|
duk_push_external_buffer(ctx); // [var][bignum][fromBuffer][this][buffer]
|
|
duk_config_buffer(ctx, -1, &v, sizeof(v));
|
|
duk_push_buffer_object(ctx, -1, 0, sizeof(v), DUK_BUFOBJ_NODEJS_BUFFER);// [var][bignum][fromBuffer][this][buffer][nodebuffer]
|
|
duk_remove(ctx, -2); // [var][bignum][fromBuffer][this][nodeBuffer]
|
|
duk_push_object(ctx); // [var][bignum][fromBuffer][this][nodeBuffer][options
|
|
duk_push_string(ctx, LE ? "little" : "big"); // [var][bignum][fromBuffer][this][nodeBuffer][options][endian]
|
|
duk_put_prop_string(ctx, -2, "endian");
|
|
duk_call_method(ctx, 2); // [var][bignum][bignum]
|
|
return(1);
|
|
|
|
}
|
|
duk_ret_t ILibDuktape_WebSocket_bytesSent_uncompressed(duk_context *ctx)
|
|
{
|
|
int16_t test = 0x0001;
|
|
int LE = ((char*)&test)[0] ? 1 : 0;
|
|
|
|
ILibDuktape_WebSocket_State *ws = NULL;
|
|
duk_push_this(ctx); // [WebSocket_Decoded]
|
|
if (!duk_has_prop_string(ctx, -1, ILibDuktape_WSDEC2WS))
|
|
{
|
|
duk_push_null(ctx);
|
|
return(1);
|
|
}
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WSDEC2WS); // [WebSocket_Decoded][WebSocket]
|
|
ws = (ILibDuktape_WebSocket_State*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_WebSocket_StatePtr);
|
|
|
|
uint64_t v = ws->uncompressedSent;
|
|
duk_eval_string(ctx, "require('bignum')"); // [var][bignum]
|
|
duk_prepare_method_call(ctx, -1, "fromBuffer"); // [var][bignum][fromBuffer][this]
|
|
duk_push_external_buffer(ctx); // [var][bignum][fromBuffer][this][buffer]
|
|
duk_config_buffer(ctx, -1, &v, sizeof(v));
|
|
duk_push_buffer_object(ctx, -1, 0, sizeof(v), DUK_BUFOBJ_NODEJS_BUFFER);// [var][bignum][fromBuffer][this][buffer][nodebuffer]
|
|
duk_remove(ctx, -2); // [var][bignum][fromBuffer][this][nodeBuffer]
|
|
duk_push_object(ctx); // [var][bignum][fromBuffer][this][nodeBuffer][options
|
|
duk_push_string(ctx, LE ? "little" : "big"); // [var][bignum][fromBuffer][this][nodeBuffer][options][endian]
|
|
duk_put_prop_string(ctx, -2, "endian");
|
|
duk_call_method(ctx, 2); // [var][bignum][bignum]
|
|
return(1);
|
|
|
|
}
|
|
duk_ret_t ILibDuktape_WebSocket_bytesSent_actual(duk_context *ctx)
|
|
{
|
|
int16_t test = 0x0001;
|
|
int LE = ((char*)&test)[0] ? 1 : 0;
|
|
|
|
ILibDuktape_WebSocket_State *ws = NULL;
|
|
duk_push_this(ctx); // [WebSocket_Decoded]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WSDEC2WS); // [WebSocket_Decoded][WebSocket]
|
|
ws = (ILibDuktape_WebSocket_State*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_WebSocket_StatePtr);
|
|
|
|
uint64_t v = ws->actualSent;
|
|
duk_eval_string(ctx, "require('bignum')"); // [var][bignum]
|
|
duk_prepare_method_call(ctx, -1, "fromBuffer"); // [var][bignum][fromBuffer][this]
|
|
duk_push_external_buffer(ctx); // [var][bignum][fromBuffer][this][buffer]
|
|
duk_config_buffer(ctx, -1, &v, sizeof(v));
|
|
duk_push_buffer_object(ctx, -1, 0, sizeof(v), DUK_BUFOBJ_NODEJS_BUFFER);// [var][bignum][fromBuffer][this][buffer][nodebuffer]
|
|
duk_remove(ctx, -2); // [var][bignum][fromBuffer][this][nodeBuffer]
|
|
duk_push_object(ctx); // [var][bignum][fromBuffer][this][nodeBuffer][options
|
|
duk_push_string(ctx, LE ? "little" : "big"); // [var][bignum][fromBuffer][this][nodeBuffer][options][endian]
|
|
duk_put_prop_string(ctx, -2, "endian");
|
|
duk_call_method(ctx, 2); // [var][bignum][bignum]
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_WebSocket_bytesSent_ratio(duk_context *ctx)
|
|
{
|
|
ILibDuktape_WebSocket_State *ws = NULL;
|
|
duk_push_this(ctx); // [WebSocket_Decoded]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WSDEC2WS); // [WebSocket_Decoded][WebSocket]
|
|
ws = (ILibDuktape_WebSocket_State*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_WebSocket_StatePtr);
|
|
|
|
long double ratio = (long double)ws->actualSent / (long double)ws->uncompressedSent;
|
|
ratio = (1 - ratio) * 100;
|
|
duk_push_number(ctx, floor((double)ratio));
|
|
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_WebSocket_bytesReceived_ratio(duk_context *ctx)
|
|
{
|
|
ILibDuktape_WebSocket_State *ws = NULL;
|
|
duk_push_this(ctx); // [WebSocket_Decoded]
|
|
duk_get_prop_string(ctx, -1, ILibDuktape_WSDEC2WS); // [WebSocket_Decoded][WebSocket]
|
|
ws = (ILibDuktape_WebSocket_State*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_WebSocket_StatePtr);
|
|
|
|
long double ratio = (long double)ws->actualReceived / (long double)ws->uncompressedReceived;
|
|
ratio = (1 - ratio) * 100;
|
|
duk_push_number(ctx, floor((double)ratio));
|
|
|
|
return(1);
|
|
}
|
|
duk_ret_t ILibDuktape_httpStream_webSocketStream_new(duk_context *ctx)
|
|
{
|
|
int narg = duk_get_top(ctx);
|
|
ILibDuktape_WebSocket_State *state;
|
|
duk_push_object(ctx); // [WebSocket]
|
|
ILibDuktape_WriteID(ctx, "http.WebSocketStream");
|
|
state = (ILibDuktape_WebSocket_State*)Duktape_PushBuffer(ctx, sizeof(ILibDuktape_WebSocket_State)); // [WebSocket][data]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_WebSocket_StatePtr); // [WebSocket]
|
|
state->ctx = ctx;
|
|
state->ObjectPtr = duk_get_heapptr(ctx, -1);
|
|
state->chain = Duktape_GetChain(ctx);
|
|
if (narg > 1 && duk_is_object(ctx, 1))
|
|
{
|
|
state->permessageDeflate = Duktape_GetIntPropertyValue(ctx, 1, "perMessageDeflate", 0);
|
|
state->minimumThreshold = Duktape_GetIntPropertyValue(ctx, 1, "minimumThreshold", 64);
|
|
state->maxSkipCount = Duktape_GetIntPropertyValue(ctx, 1, "maxSkipCount", 128);
|
|
state->minSkipCount = Duktape_GetIntPropertyValue(ctx, 1, "minSkipCount", 10);
|
|
state->cWBITS = Duktape_GetIntPropertyValue(ctx, 1, "clientMaxWindowBits", 15) * (-1);
|
|
state->sWBITS = Duktape_GetIntPropertyValue(ctx, 1, "serverMaxWindowBits", 15) * (-1);
|
|
state->skipCount = 0;
|
|
state->nonCompressibleCount = 0;
|
|
}
|
|
|
|
duk_push_object(ctx); // [WebSocket][Encoded]
|
|
ILibDuktape_WriteID(ctx, "http.WebSocketStream.encoded");
|
|
state->encodedStream = ILibDuktape_DuplexStream_InitEx(ctx, ILibDuktape_httpStream_webSocket_EncodedWriteSink, ILibDuktape_httpStream_webSocket_EncodedEndSink, ILibDuktape_httpStream_webSocket_EncodedPauseSink, ILibDuktape_httpStream_webSocket_EncodedResumeSink, ILibDuktape_httpStream_webSocket_EncodedUnshiftSink, state);
|
|
|
|
ILibDuktape_EventEmitter_AddOn_Infrastructure(ctx, -1, "pipe", ILibDuktape_httpStream_webSocketStream_encodedPiped);
|
|
|
|
duk_dup(ctx, -2); // [WebSocket][Encoded][WebSocket]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_WSENC2WS); // [WebSocket][Encoded]
|
|
ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "~", ILibDuktape_httpStream_webSocketStream_encoded_Finalizer);
|
|
|
|
ILibDuktape_CreateReadonlyProperty(ctx, "encoded"); // [WebSocket]
|
|
duk_push_object(ctx); // [WebSocket][Decoded]
|
|
ILibDuktape_WriteID(ctx, "http.WebSocketStream.decoded");
|
|
duk_dup(ctx, -2); // [WebSocket][Decoded][WebSocket]
|
|
duk_put_prop_string(ctx, -2, ILibDuktape_WSDEC2WS); // [WebSocket][Decoded]
|
|
|
|
state->decodedStream = ILibDuktape_DuplexStream_InitEx(ctx, ILibDuktape_httpStream_webSocket_DecodedWriteSink, ILibDuktape_httpStream_webSocket_DecodedEndSink, ILibDuktape_httpStream_webSocket_DecodedPauseSink, ILibDuktape_httpStream_webSocket_DecodedResumeSink, ILibDuktape_httpStream_webSocket_DecodedUnshiftSink, state);
|
|
ILibDuktape_EventEmitter_CreateEventEx(ILibDuktape_EventEmitter_GetEmitter(ctx, -1), "ping");
|
|
ILibDuktape_EventEmitter_CreateEventEx(ILibDuktape_EventEmitter_GetEmitter(ctx, -1), "pong");
|
|
ILibDuktape_EventEmitter_CreateEventEx(ILibDuktape_EventEmitter_GetEmitter(ctx, -1), "timeout");
|
|
ILibDuktape_CreateProperty_InstanceMethod(ctx, "ping", ILibDuktape_httpStream_webSocketStream_sendPing, DUK_VARARGS);
|
|
ILibDuktape_CreateProperty_InstanceMethod(ctx, "pong", ILibDuktape_httpStream_webSocketStream_sendPong, DUK_VARARGS);
|
|
#ifdef _SSL_KEYS_EXPORTABLE
|
|
ILibDuktape_CreateInstanceMethod(ctx, "_exportKeys", ILibDuktape_httpStream_webSocket_exportKeys, 0);
|
|
#endif
|
|
|
|
ILibDuktape_CreateEventWithSetterEx(ctx, "descriptorMetadata", ILibDuktape_httpStream_webSocketStream_descriptorMetadata);
|
|
ILibDuktape_CreateEventWithGetter(ctx, "bytesReceived_uncompressed", ILibDuktape_WebSocket_bytesReceived_uncompressed);
|
|
ILibDuktape_CreateEventWithGetter(ctx, "bytesReceived_actual", ILibDuktape_WebSocket_bytesReceived_actual);
|
|
ILibDuktape_CreateEventWithGetter(ctx, "bytesSent_uncompressed", ILibDuktape_WebSocket_bytesSent_uncompressed);
|
|
ILibDuktape_CreateEventWithGetter(ctx, "bytesSent_actual", ILibDuktape_WebSocket_bytesSent_actual);
|
|
ILibDuktape_CreateEventWithGetter(ctx, "bytesSent_ratio", ILibDuktape_WebSocket_bytesSent_ratio);
|
|
ILibDuktape_CreateEventWithGetter(ctx, "bytesReceived_ratio", ILibDuktape_WebSocket_bytesReceived_ratio);
|
|
|
|
ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "~", ILibDuktape_httpStream_webSocketStream_decoded_Finalizer);
|
|
|
|
|
|
|
|
ILibDuktape_CreateReadonlyProperty(ctx, "decoded"); // [WebSocket]
|
|
ILibDuktape_CreateFinalizer(ctx, ILibDuktape_httpStream_webSocketStream_finalizer);
|
|
return(1);
|
|
}
|
|
|
|
duk_ret_t ILibDuktape_http_generateNonce(duk_context *ctx)
|
|
{
|
|
int len = (int)duk_require_int(ctx, 0);
|
|
if ((len+1) < sizeof(ILibScratchPad))
|
|
{
|
|
util_randomtext(len, ILibScratchPad);
|
|
ILibScratchPad[len] = 0;
|
|
duk_push_string(ctx, ILibScratchPad);
|
|
return(1);
|
|
}
|
|
else
|
|
{
|
|
return(ILibDuktape_Error(ctx, "Specified length is too long. Please Specify a value < %llu", (uint64_t)sizeof(ILibScratchPad)));
|
|
}
|
|
}
|
|
duk_ret_t ILibDuktape_http_resolve(duk_context *ctx)
|
|
{
|
|
duk_push_sprintf(ctx, "resolve('%s');", duk_require_string(ctx, 0));
|
|
duk_eval(ctx);
|
|
return(1);
|
|
}
|
|
void ILibDuktape_HttpStream_http_PUSH(duk_context *ctx, void *chain)
|
|
{
|
|
duk_push_object(ctx); // [http]
|
|
ILibDuktape_WriteID(ctx, "http");
|
|
ILibDuktape_CreateInstanceMethod(ctx, "request", ILibDuktape_HttpStream_http_request, DUK_VARARGS);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "createServer", ILibDuktape_HttpStream_http_createServer, DUK_VARARGS);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "get", ILibDuktape_HttpStream_http_get, DUK_VARARGS);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "createStream", ILibduktape_HttpStream_create, DUK_VARARGS);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "Agent", ILibDuktape_HttpStream_Agent_new, DUK_VARARGS);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "parseUri", ILibDuktape_httpStream_parseUri, 1);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "webSocketStream", ILibDuktape_httpStream_webSocketStream_new, DUK_VARARGS);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "generateNonce", ILibDuktape_http_generateNonce, 1);
|
|
ILibDuktape_CreateInstanceMethod(ctx, "resolve", ILibDuktape_http_resolve, 1);
|
|
|
|
// HTTP Global Agent
|
|
duk_push_c_function(ctx, ILibDuktape_HttpStream_Agent_new, DUK_VARARGS); // [http][newAgent]
|
|
duk_dup(ctx, -2); // [http][newAgent][this]
|
|
duk_call_method(ctx, 0); // [http][Agent]
|
|
duk_put_prop_string(ctx, -2, "globalAgent"); // [http][Agent]
|
|
}
|
|
void ILibDuktape_HttpStream_https_PUSH(duk_context *ctx, void *chain)
|
|
{
|
|
ILibDuktape_HttpStream_http_PUSH(ctx, chain); // [https]
|
|
ILibDuktape_WriteID(ctx, "https");
|
|
duk_push_boolean(ctx, 1);
|
|
ILibDuktape_CreateReadonlyProperty(ctx, "isHTTPS"); // [https]
|
|
}
|
|
void ILibDuktape_HttpStream_Init(duk_context *ctx)
|
|
{
|
|
ILibDuktape_ModSearch_AddHandler(ctx, "http", ILibDuktape_HttpStream_http_PUSH);
|
|
ILibDuktape_ModSearch_AddHandler(ctx, "https", ILibDuktape_HttpStream_https_PUSH);
|
|
}
|