1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-06 00:13:33 +00:00
Files
MeshAgent/microscript/ILibDuktape_http.c
2017-10-12 14:28:03 -07:00

2976 lines
116 KiB
C

/*
Copyright 2006 - 2017 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, software
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 "ILibDuktape_http.h"
#include "ILibDuktape_net.h"
#include "ILibDuktapeModSearch.h"
#include "microstack/ILibWebServer.h"
#include "microstack/ILibWebClient.h"
#include "ILibDuktape_ReadableStream.h"
#include "ILibDuktape_Helpers.h"
#include "ILibDuktape_WritableStream.h"
#include "ILibDuktape_DuplexStream.h"
#include "ILibDuktape_EventEmitter.h"
#include "microstack/ILibCrypto.h"
#define HTTP_WEBCLIENT_MGR "_RequestManagerPtr"
#define NativeSessionPtr "\xFF_SessionPtr"
#define SessionPtrJS "\xFF_SessionPtr_JS"
#define TLSPTR "\xFF_tlsSettings"
#define TLS_CERT "\xFF_cert"
#define TLS_CERT_NON_LEAF "\xFF_cert_nonleaf"
#define HTTP_DEFAULT_PROTO_KEY "\xFF_defaultProto"
#define HTTP_REQUEST_TOKEN_PTR "_TokenPtr"
#define HTTP_SOCKET_PTRS "\xFF_socket_ptrs"
#define HTTP_SOCKET_BUFFERPTR "\xFF_socket_bufferptr"
#define HTTP_STREAM_WRAPPER "\xFF_http_StreamWrapper"
#define HTTP_STREAM_WRAPPER_BUFSIZE 4096
#define HTTP_CLIENTREQUEST_PARAMETER "\xFF_http_clientRequest_parameter"
#define CLIENTREQUEST_HTTP "\xFF_clientRequest_HTTP"
#define HTTP_INCOMINGMSG_WebStateObject "\xFF_incomingMessage_WebStateObject"
#define DIGEST_USERNAME "\xFF_DigestUsername"
#define DIGEST_PASSWORD "\xFF_DigestPassword"
#define HTTP_DIGEST "\xFF_HTTP_DIGEST"
#define DIGEST_CLIENT_REQUEST "\xFF_DIGEST_CLIENT_REQUEST"
#define HTTP_CLIENTREQUEST_DATAPTR "\xFF_CLIENTREQUEST_DATAPTR"
extern duk_idx_t ILibWebServer_DukTape_Push_ILibWebServerSession(duk_context *ctx, ILibWebServer_Session *session);
void* ILibDuktape_http_request_PUSH_clientRequest(duk_context *ctx, ILibWebClient_RequestToken token, int isWebSocket);
typedef enum ILibDuktape_http_request_dataTypes
{
ILibDuktape_http_request_dataType_UNKNOWN = 0,
ILibDuktape_http_request_dataType_request = 1,
ILibDuktape_http_request_dataType_webSocket = 2
}ILibDuktape_http_request_dataTypes;
#pragma pack(push, 1)
typedef struct ILibDuktape_http_request_dataType
{
ILibDuktape_http_request_dataTypes STRUCT_TYPE;
}ILibDuktape_http_request_dataType;
typedef struct ILibDuktape_http_requestClient_callbacks
{
ILibDuktape_http_request_dataTypes STRUCT_TYPE;
void *clientRequest;
void *readableStream;
void *requestStream;
void *OnReceive;
#ifndef MICROSTACK_NOTLS
int rejectUnauthorized;
void *checkServerIdentity;
#endif
}ILibDuktape_http_requestClient_callbacks;
typedef struct ILibDuktape_http_server_session_ptrs
{
void *ctx;
void *serverObject;
void *OnResponse;
void *OnUpgrade;
void *OnConnect;
}ILibDuktape_http_server_ptrs;
typedef struct ILibDuktape_http_session_ptrs
{
ILibDuktape_readableStream *req;
ILibDuktape_WritableStream *res;
}ILibDuktape_http_session_ptrs;
typedef struct ILibDuktape_WebSocket_Pointers
{
ILibDuktape_http_request_dataTypes STRUCT_TYPE;
duk_context *ctx;
void *clientRequest_ptr;
void *socket_ptr;
ILibDuktape_DuplexStream *stream;
ILibWebClient_StateObject *wcdo;
int timeout;
void *onTimeout;
void *onPing;
void *onPong;
#ifndef MICROSTACK_NOTLS
int rejectUnauthorized;
void *checkServerIdentity;
#endif
}ILibDuktape_WebSocket_Pointers;
#pragma pack(pop)
typedef struct ILibDuktape_http_rawSocket
{
duk_context *ctx;
void *self;
void *ConnectionToken;
ILibDuktape_EventEmitter *emitter;
ILibDuktape_DuplexStream *stream;
}ILibDuktape_http_rawSocket;
typedef struct ILibDuktape_http_streamWrapper
{
duk_context *ctx;
void *self;
ILibDuktape_EventEmitter *emitter;
ILibDuktape_DuplexStream *ds;
ILibWebClient_StateObject *wcdo;
ILibHTTPPacket *impliedHeaders;
ILibDuktape_WritableStream *serverResponse_stream;
void *OnRequest;
void *OnResponse;
int chunkEncoded;
void *PipedReader;
ILibDuktape_WritableStream *PipedWriter;
char reserved[sizeof(ILibTransport) + sizeof(void*)];
char hex[16];
char buffer[HTTP_STREAM_WRAPPER_BUFSIZE];
int bufferLen;
}ILibDuktape_http_streamWrapper;
char * ILibDuktape_http_getDefaultProto(duk_context *ctx)
{
char *retVal;
duk_push_this(ctx); // [http]
duk_get_prop_string(ctx, -1, HTTP_DEFAULT_PROTO_KEY); // [http][default]
retVal = (char*)duk_get_string(ctx, -1);
duk_pop_2(ctx); // ...
return retVal;
}
#ifndef MICROSTACK_NOTLS
void ILibDuktape_X509_PUSH(duk_context *ctx, X509* cert)
{
char hash[32];
char fingerprint[100];
util_keyhash2(cert, hash);
util_tohex2(hash, 32, fingerprint);
duk_push_object(ctx); // [cert]
duk_push_string(ctx, fingerprint); // [cert][fingerprint]
duk_put_prop_string(ctx, -2, "fingerprint"); // [cert]
}
#endif
ILibWebClient_RequestManager ILibDuktape_http_GetRequestManager(duk_context *ctx)
{
ILibWebClient_RequestManager retVal = NULL;
duk_push_this(ctx); // [http]
if (duk_has_prop_string(ctx, -1, HTTP_WEBCLIENT_MGR))
{
duk_get_prop_string(ctx, -1, HTTP_WEBCLIENT_MGR); // [http][mgr]
retVal = (ILibWebClient_RequestManager)duk_get_pointer(ctx, -1);
duk_pop_2(ctx); // ...
}
else
{
duk_get_prop_string(ctx, -1, "chain"); // [http][chain]
duk_get_prop_string(ctx, -2, "RequestPoolSize"); // [http][chain][poolSize]
retVal = ILibCreateWebClient(duk_get_int(ctx, -1), duk_get_pointer(ctx, -2));
duk_pop_2(ctx); // [http]
duk_push_pointer(ctx, retVal); // [http][mgr]
duk_put_prop_string(ctx, -2, HTTP_WEBCLIENT_MGR); // [http]
duk_pop(ctx); // ...
}
return retVal;
}
duk_ret_t ILibDuktape_http_serverresponse_end(duk_context *ctx)
{
ILibWebServer_Session *session;
ILibHTTPPacket *packet;
int statusCode;
char *statusMessage;
duk_size_t statusMessageLen;
int nargs = duk_get_top(ctx);
duk_push_this(ctx); // [res]
duk_get_prop_string(ctx, -1, "SessionPtr"); // [res][session]
session = (ILibWebServer_Session*)duk_to_pointer(ctx, -1); // [res][session]
duk_pop(ctx); // [res]
duk_get_prop_string(ctx, -1, "PacketPtr"); // [res][packet]
packet = (ILibHTTPPacket*)duk_to_pointer(ctx, -1); // [res][packet]
duk_pop(ctx); // [res]
duk_get_prop_string(ctx, -1, "statusCode"); // [res][code]
statusCode = duk_to_int(ctx, -1); duk_pop(ctx); // [res]
duk_get_prop_string(ctx, -1, "statusMessage"); // [res][msg]
statusMessage = (char*)duk_get_lstring(ctx, -1, &statusMessageLen); duk_pop(ctx); // [res]
ILibSetStatusCode(packet, statusCode, statusMessage, (int)statusMessageLen);
((ILibDuktape_http_session_ptrs*)session->Reserved_Transport.ChainLink.ExtraMemoryPtr)->req = NULL;
if (nargs == 0)
{
ILibWebServer_Send(session, packet);
return 0;
}
ILibWebServer_StreamHeader(session, packet);
if (nargs > 0)
{
char *body;
duk_size_t bodyLen = 0;
if (duk_is_string(ctx, 0))
{
body = (char*)duk_get_lstring(ctx, 0, &bodyLen);
}
else
{
body = Duktape_GetBuffer(ctx, 0, &bodyLen);
}
ILibWebServer_StreamBody(session, body, (int)bodyLen, ILibAsyncSocket_MemoryOwnership_USER, ILibWebServer_DoneFlag_Done);
}
return 0;
}
duk_ret_t ILibDuktape_http_serverresponse_setHeader(duk_context *ctx)
{
ILibHTTPPacket *packet;
if (!duk_is_string(ctx, 0) || !duk_is_string(ctx, 1)) { return(ILibDuktape_Error(ctx, "http.serverresponse.setHeader(): Invalid Parameters")); }
duk_size_t fieldLen, valueLen;
char *field = (char*)duk_get_lstring(ctx, 0, &fieldLen);
char *value = (char*)duk_get_lstring(ctx, 1, &valueLen);
duk_push_this(ctx);
duk_get_prop_string(ctx, -1, "PacketPtr");
packet = (ILibHTTPPacket*)duk_to_pointer(ctx, -1);
ILibAddHeaderLine(packet, field, (int)fieldLen, value, (int)valueLen);
return 0;
}
duk_ret_t ILibDuktape_http_serverresponse_writeHead(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
int statusCode;
char *statusMessage = nargs > 1 ? (char*)duk_require_string(ctx, 1) : "OK";
ILibHTTPPacket *packet;
ILibWebServer_Session *session;
duk_push_this(ctx); // [response]
duk_get_prop_string(ctx, -1, "PacketPtr"); // [response][packet]
packet = (ILibHTTPPacket*)duk_to_pointer(ctx, -1);
duk_del_prop_string(ctx, -2, "PacketPtr");
duk_get_prop_string(ctx, -2, "SessionPtr"); // [response][packet][session]
session = (ILibWebServer_Session*)duk_to_pointer(ctx, -1);
if (nargs < 1) { duk_push_string(ctx, "Missing Status Code"); duk_throw(ctx); return(DUK_RET_ERROR); }
statusCode = duk_require_int(ctx, 0);
ILibSetStatusCode(packet, statusCode, statusMessage, -1);
if (nargs > 2)
{
duk_enum(ctx, 2, 0);
while (duk_next(ctx, -1, 1))
{
ILibAddHeaderLine(packet, duk_to_string(ctx, -2), -1, duk_to_string(ctx, -1), -1); // [enum][key][value]
duk_pop_2(ctx); // [enum]
}
}
ILibWebServer_StreamHeader(session, packet);
return 0;
}
ILibTransport_DoneState ILibDuktape_http_server_WriteResponse(ILibDuktape_WritableStream *stream, char *buffer, int bufferLen, void *user)
{
ILibWebServer_Status status = ILibWebServer_ALL_DATA_SENT;
ILibWebServer_Session *session = (ILibWebServer_Session*)user;
duk_push_heapptr(stream->ctx, stream->obj); // [res]
if (duk_has_prop_string(stream->ctx, -1, "PacketPtr"))
{
ILibHTTPPacket *packet;
duk_get_prop_string(stream->ctx, -1, "PacketPtr"); // [res][packet]
packet = (ILibHTTPPacket*)duk_to_pointer(stream->ctx, -1);
duk_pop(stream->ctx); // [res]
duk_del_prop_string(stream->ctx, -1, "PacketPtr");
duk_get_prop_string(stream->ctx, -1, "statusCode"); // [res][code]
duk_get_prop_string(stream->ctx, -2, "statusMessage"); // [res][code][message]
ILibSetStatusCode(packet, duk_to_int(stream->ctx, -2), (char*)duk_to_string(stream->ctx, -1), -1);
duk_pop_3(stream->ctx); // ...
status = ILibWebServer_StreamHeader(session, packet);
}
if (bufferLen > 0)
{
status = ILibWebServer_StreamBody(session, buffer, bufferLen, ILibAsyncSocket_MemoryOwnership_USER, ILibWebServer_DoneFlag_NotDone);
}
switch (status)
{
case ILibWebServer_ALL_DATA_SENT:
return(ILibTransport_DoneState_COMPLETE);
case ILibWebServer_NOT_ALL_DATA_SENT_YET:
return(ILibTransport_DoneState_INCOMPLETE);
default:
return(ILibTransport_DoneState_ERROR);
}
}
void ILibDuktape_http_server_EndResponse(ILibDuktape_WritableStream *stream, void *user)
{
ILibWebServer_Session *session = (ILibWebServer_Session*)user;
ILibDuktape_http_server_WriteResponse(stream, NULL, 0, user);
ILibWebServer_StreamBody(session, NULL, 0, ILibAsyncSocket_MemoryOwnership_USER, ILibWebServer_DoneFlag_Done);
}
duk_ret_t ILibDuktape_http_server_ServerResponse_Finalizer(duk_context *ctx)
{
if (duk_has_prop_string(ctx, 0, "PacketPtr"))
{
duk_get_prop_string(ctx, 0, "PacketPtr");
ILibDestructPacket((ILibHTTPPacket*)duk_to_pointer(ctx, -1));
duk_del_prop_string(ctx, 0, "PacketPtr");
}
return 0;
}
ILibDuktape_WritableStream* ILibDuktape_http_server_PUSH_ServerResponse(duk_context *ctx, ILibWebServer_Session *session)
{
ILibHTTPPacket *packet = ILibCreateEmptyPacket();
ILibSetVersion(packet, "1.1", 3);
duk_push_object(ctx); // [obj]
duk_push_pointer(ctx, session); // [obj][session]
duk_put_prop_string(ctx, -2, "SessionPtr"); // [obj]
duk_push_pointer(ctx, packet); // [obj][packet]
duk_put_prop_string(ctx, -2, "PacketPtr"); // [obj]
duk_push_int(ctx, 500); // [obj][statusCode]
duk_put_prop_string(ctx, -2, "statusCode"); // [obj]
duk_push_string(ctx, ""); // [obj][statusMessage]
duk_put_prop_string(ctx, -2, "statusMessage"); // [obj]
duk_push_c_function(ctx, ILibDuktape_http_server_ServerResponse_Finalizer, 1); // [obj][fin]
duk_set_finalizer(ctx, -2); // [obj]
duk_push_c_function(ctx, ILibDuktape_http_serverresponse_setHeader, 2); // [obj][func]
duk_put_prop_string(ctx, -2, "setHeader"); // [obj]
duk_push_c_function(ctx, ILibDuktape_http_serverresponse_writeHead, DUK_VARARGS); // [obj][func]
duk_put_prop_string(ctx, -2, "writeHead"); // [obj]
return ILibDuktape_WritableStream_Init(ctx, ILibDuktape_http_server_WriteResponse, ILibDuktape_http_server_EndResponse, session);
}
ILibWebServer_Session* ILibDuktape_http_server_NativeSession_GetSession(duk_context *ctx)
{
ILibWebServer_Session *retVal = NULL;
duk_push_this(ctx); // [session]
duk_get_prop_string(ctx, -1, NativeSessionPtr); // [session][ptr]
retVal = (ILibWebServer_Session*)duk_get_pointer(ctx, -1);
duk_pop_2(ctx); // ...
return retVal;
}
duk_ret_t ILibDuktape_http_server_NativeSession_Digest_IsAuthenticated(duk_context *ctx)
{
ILibWebServer_Session *session = ILibDuktape_http_server_NativeSession_GetSession(ctx);
if (!duk_is_string(ctx, 0)) { return(ILibDuktape_Error(ctx, "server.NativeSession.IsAuthenticated(): Invalid Parameters")); }
duk_size_t realmLen;
char *realm = (char*)duk_get_lstring(ctx, 0, &realmLen);
duk_push_int(ctx, ILibWebServer_Digest_IsAuthenticated(session, realm, (int)realmLen));
return 1;
}
duk_ret_t ILibDuktape_http_server_NativeSession_Digest_SendUnAuthorized(duk_context *ctx)
{
ILibWebServer_Session *session = ILibDuktape_http_server_NativeSession_GetSession(ctx);
if (!duk_is_string(ctx, 0) || !duk_is_string(ctx, 1)) { return(ILibDuktape_Error(ctx, "server.NativeSession.SendUnAuthorized(): Invalid Parameters")); }
duk_size_t realmLen, htmlLen;
char *realm = (char*)duk_get_lstring(ctx, 0, &realmLen);
char *html = (char*)duk_get_lstring(ctx, 1, &htmlLen);
ILibDuktape_http_session_ptrs *sessionPtrs = (ILibDuktape_http_session_ptrs*)session->Reserved_Transport.ChainLink.ExtraMemoryPtr;
ILibWebServer_Digest_SendUnauthorized(session, realm, (int)realmLen, html, (int)htmlLen);
sessionPtrs->req = NULL;
return 0;
}
duk_ret_t ILibDuktape_http_server_NativeSession_Digest_GetUsername(duk_context *ctx)
{
ILibWebServer_Session *session = ILibDuktape_http_server_NativeSession_GetSession(ctx);
duk_push_string(ctx, ILibWebServer_Digest_GetUsername(session));
return 1;
}
duk_ret_t ILibDuktape_http_server_NativeSession_Digest_ValidatePassword(duk_context *ctx)
{
ILibWebServer_Session *session = ILibDuktape_http_server_NativeSession_GetSession(ctx);
duk_size_t pwdLen;
char *pwd = (char*)duk_get_lstring(ctx, 0, &pwdLen);
if (!duk_is_string(ctx, 0)) { return(ILibDuktape_Error(ctx, "server.NativeSession.ValidatePassword(): Invalid Parameter")); }
duk_push_int(ctx, ILibWebServer_Digest_ValidatePassword(session, pwd, (int)pwdLen));
return 1;
}
duk_ret_t ILibDuktape_http_server_NativeSession_WebSocket_GetDataType(duk_context *ctx)
{
ILibWebServer_WebSocket_DataTypes dType;
ILibWebServer_Session *session;
duk_push_this(ctx);
duk_get_prop_string(ctx, -1, NativeSessionPtr);
session = (ILibWebServer_Session*)duk_get_pointer(ctx, -1);
dType = ILibWebServer_WebSocket_GetDataType(session);
duk_push_int(ctx, (int)dType);
return(1);
}
duk_ret_t ILibDuktape_http_server_NativeSession_WebSocket_Upgrade(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
int maxBuffer = nargs > 0 ? (int)duk_require_int(ctx, 0) : 65535;
ILibWebServer_Session *session;
duk_push_this(ctx);
duk_get_prop_string(ctx, -1, NativeSessionPtr);
session = (ILibWebServer_Session*)duk_get_pointer(ctx, -1);
ILibWebServer_UpgradeWebSocket(session, maxBuffer);
return(0);
}
void ILibDuktape_http_server_NativeSession_PUSH(duk_context *ctx, ILibWebServer_Session* session)
{
if (session->User != NULL) { duk_push_heapptr(ctx, session->User); return; }
duk_push_object(ctx); // [session]
duk_push_pointer(ctx, session); // [session][ptr]
duk_put_prop_string(ctx, -2, NativeSessionPtr); // [session]
session->User = duk_get_heapptr(ctx, -1);
ILibDuktape_CreateInstanceMethod(ctx, "Digest_IsAuthenticated", ILibDuktape_http_server_NativeSession_Digest_IsAuthenticated, 1);
ILibDuktape_CreateInstanceMethod(ctx, "Digest_SendUnauthorized", ILibDuktape_http_server_NativeSession_Digest_SendUnAuthorized, 2);
ILibDuktape_CreateInstanceMethod(ctx, "Digest_GetUsername", ILibDuktape_http_server_NativeSession_Digest_GetUsername, 0);
ILibDuktape_CreateInstanceMethod(ctx, "Digest_ValidatePassword", ILibDuktape_http_server_NativeSession_Digest_ValidatePassword, 1);
ILibDuktape_CreateInstanceMethod(ctx, "WebSocket_GetDataType", ILibDuktape_http_server_NativeSession_WebSocket_GetDataType, 0);
ILibDuktape_CreateInstanceMethod(ctx, "WebSocket_Upgrade", ILibDuktape_http_server_NativeSession_WebSocket_Upgrade, DUK_VARARGS);
}
duk_ret_t ILibDuktape_http_server_NativeSession_Getter(duk_context *ctx)
{
ILibWebServer_Session *session;
duk_push_this(ctx); // [incomingMessage]
duk_get_prop_string(ctx, -1, NativeSessionPtr); // [incomingMessage][ptr]
session = (ILibWebServer_Session*)duk_get_pointer(ctx, -1);
duk_pop(ctx); // [incomingMessage]
ILibDuktape_http_server_NativeSession_PUSH(ctx, session); // [incomingMessage][session]
if (!duk_has_prop_string(ctx, -2, SessionPtrJS))
{
duk_dup(ctx, -1); // [incomingMessage][session][session]
duk_put_prop_string(ctx, -3, SessionPtrJS); // [incomingMessage][session]
}
return 1;
}
ILibTransport_DoneState ILibDutkape_http_server_rawSocket_WriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user)
{
ILibDuktape_http_rawSocket *ptrs = (ILibDuktape_http_rawSocket*)user;
return((ILibTransport_DoneState)ILibAsyncSocket_Send(ptrs->ConnectionToken, buffer, bufferLen, ILibAsyncSocket_MemoryOwnership_USER));
}
void ILibDuktape_http_server_rawSocket_OnData(ILibAsyncSocket_SocketModule socketModule, char* buffer, int *p_beginPointer, int endPointer, ILibAsyncSocket_OnInterrupt* OnInterrupt, void **user, int *PAUSE)
{
ILibDuktape_http_rawSocket *ptrs = (ILibDuktape_http_rawSocket*)user;
buffer += *p_beginPointer;
*p_beginPointer = endPointer;
*PAUSE = ILibDuktape_DuplexStream_WriteData(ptrs->stream, buffer, endPointer);
}
void ILibDuktape_http_server_rawSocket_OnDisconnect(ILibAsyncSocket_SocketModule socketModule, void *user)
{
ILibDuktape_http_rawSocket *ptrs = (ILibDuktape_http_rawSocket*)user;
ILibDuktape_DuplexStream_WriteEnd(ptrs->stream);
}
void ILibDuktape_http_server_rawSocket_OnSendOK(ILibAsyncSocket_SocketModule socketModule, void *user)
{
ILibDuktape_http_rawSocket *ptrs = (ILibDuktape_http_rawSocket*)user;
ILibDuktape_DuplexStream_Ready(ptrs->stream);
}
void ILibDuktape_http_server_rawSocket_EndSink(ILibDuktape_DuplexStream *stream, void *user)
{
ILibDuktape_http_rawSocket *ptrs = (ILibDuktape_http_rawSocket*)user;
ILibAsyncSocket_Disconnect(ptrs->ConnectionToken);
}
void ILibDuktape_http_server_rawSocket_PauseSink(ILibDuktape_DuplexStream *sender, void *user)
{
ILibDuktape_http_rawSocket *ptrs = (ILibDuktape_http_rawSocket*)user;
ILibAsyncSocket_Pause(ptrs->ConnectionToken);
}
void ILibDuktape_http_server_rawSocket_ResumeSink(ILibDuktape_DuplexStream *sender, void *user)
{
ILibDuktape_http_rawSocket *ptrs = (ILibDuktape_http_rawSocket*)user;
ILibAsyncSocket_Resume(ptrs->ConnectionToken);
}
duk_ret_t ILibDuktape_http_server_IncomingMessage_rawSocket(duk_context *ctx)
{
void *ConnectionToken;
ILibDuktape_http_rawSocket *ptrs;
duk_push_this(ctx); // [incomingMessage]
if (!duk_has_prop_string(ctx, -1, NativeSessionPtr))
{
duk_push_null(ctx);
return 1;
}
duk_get_prop_string(ctx, -1, NativeSessionPtr);
ConnectionToken = ILibWebServer_Session_GetConnectionToken((ILibWebServer_Session*)duk_get_pointer(ctx, -1));
if (ConnectionToken == NULL)
{
duk_push_null(ctx);
return 1;
}
duk_push_object(ctx); // [socket]
duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_http_rawSocket)); // [socket][buffer]
ptrs = (ILibDuktape_http_rawSocket*)Duktape_GetBuffer(ctx, -1, NULL);
memset(ptrs, 0, sizeof(ILibDuktape_http_rawSocket));
duk_put_prop_string(ctx, -2, HTTP_SOCKET_BUFFERPTR); // [socket]
ptrs->ctx = ctx;
ptrs->ConnectionToken = ConnectionToken;
ptrs->self = duk_get_heapptr(ctx, -1);
ptrs->emitter = ILibDuktape_EventEmitter_Create(ctx);
ptrs->stream = ILibDuktape_DuplexStream_Init(ctx, ILibDutkape_http_server_rawSocket_WriteSink, ILibDuktape_http_server_rawSocket_EndSink, ILibDuktape_http_server_rawSocket_PauseSink, ILibDuktape_http_server_rawSocket_ResumeSink, ptrs);
ILibAsyncSocket_UpdateCallbacks(ConnectionToken, ILibDuktape_http_server_rawSocket_OnData, NULL, ILibDuktape_http_server_rawSocket_OnDisconnect, ILibDuktape_http_server_rawSocket_OnSendOK);
free(ILibAsyncSocket_GetUser(ConnectionToken));
ILibAsyncSocket_SetUser(ConnectionToken, ptrs);
return 1;
}
void ILibDuktape_http_server_PUSH_IncomingMessage(duk_context *ctx, ILibHTTPPacket *packet, ILibWebServer_Session *session)
{
duk_push_object(ctx); // [obj]
duk_push_pointer(ctx, packet); // [obj][packet]
duk_put_prop_string(ctx, -2, "PacketPtr"); // [obj]
//duk_push_external_buffer(ctx); // [obj][buffer]
//duk_put_prop_string(ctx, -2, "_buffer"); // [obj]
if (session != NULL)
{
duk_push_pointer(ctx, session); // [obj][session]
duk_put_prop_string(ctx, -2, NativeSessionPtr); // [obj]
ILibDuktape_CreateEventWithGetter(ctx, "NativeSession", ILibDuktape_http_server_NativeSession_Getter);
}
if (packet != NULL && packet->Directive != NULL)
{
packet->Directive[packet->DirectiveLength] = 0;
duk_push_string(ctx, packet->Directive); // [obj][method]
duk_put_prop_string(ctx, -2, "method");
packet->DirectiveObj[packet->DirectiveObjLength] = 0;
duk_push_string(ctx, packet->DirectiveObj); // [obj][path]
duk_put_prop_string(ctx, -2, "url"); // [obj]
}
else if(packet != NULL)
{
duk_push_int(ctx, packet->StatusCode);
duk_put_prop_string(ctx, -2, "statusCode");
if (packet->StatusData != NULL)
{
packet->StatusData[packet->StatusDataLength] = 0;
duk_push_string(ctx, packet->StatusData);
duk_put_prop_string(ctx, -2, "statusMessage");
}
}
if (packet != NULL)
{
packetheader_field_node *n = packet->FirstField;
duk_push_object(ctx); // [obj][header]
while (n != NULL)
{
duk_push_lstring(ctx, n->Field, n->FieldLength); // [obj][header][fieldName]
duk_push_lstring(ctx, n->FieldData, n->FieldDataLength); // [obj][header][fieldName][fieldValue]
duk_put_prop(ctx, -3); // [obj][header]
n = n->NextField;
}
duk_put_prop_string(ctx, -2, "header"); // [obj]
}
if (session != NULL) { ILibDuktape_CreateEventWithGetter(ctx, "socket", ILibDuktape_http_server_IncomingMessage_rawSocket); }
}
void ILibDuktape_http_server_Pause(ILibDuktape_readableStream *sender, void *user)
{
ILibWebServer_Pause((ILibWebServer_Session*)user);
}
void ILibDuktape_http_server_Resume(ILibDuktape_readableStream *sender, void *user)
{
ILibWebServer_Resume((ILibWebServer_Session*)user);
}
void ILibDuktape_http_server_PUSH_IntermediateSocket(duk_context *ctx, ILibWebServer_Session *session)
{
}
void ILibDuktape_http_server_OnReceive(struct ILibWebServer_Session *sender, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebServer_DoneFlag done)
{
ILibDuktape_http_server_ptrs* serverPtrs = (ILibDuktape_http_server_ptrs*)sender->ParentExtraMemory;
ILibDuktape_http_session_ptrs *sessionPtrs = (ILibDuktape_http_session_ptrs*)sender->Reserved_Transport.ChainLink.ExtraMemoryPtr;
int r = 0;
if (ILibGetHeaderLineEx(header, "upgrade", 7, NULL) != NULL)
{
if (serverPtrs->OnUpgrade != NULL)
{
duk_push_heapptr(serverPtrs->ctx, serverPtrs->OnUpgrade); // [func]
duk_push_heapptr(serverPtrs->ctx, serverPtrs->serverObject); // [func][this]
ILibDuktape_http_server_PUSH_IncomingMessage(serverPtrs->ctx, header, sender); // [func][this][incoming]
ILibDuktape_http_server_PUSH_IntermediateSocket(serverPtrs->ctx, sender); // [func][this][incoming][socket]
duk_push_null(serverPtrs->ctx); // [func][this][incoming][socket][head]
if (duk_pcall_method(serverPtrs->ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(serverPtrs->ctx, "Server.OnUpgrade(): "); }
duk_pop(serverPtrs->ctx); // ...
//ILibDuktape_EventEmitter_RemoveAllListeners();
}
}
// The very first time, Server.request event will be emitted
if (sessionPtrs->req == NULL && serverPtrs->OnResponse != NULL)
{
duk_push_heapptr(serverPtrs->ctx, serverPtrs->OnResponse); // [func]
ILibDuktape_http_server_PUSH_IncomingMessage(serverPtrs->ctx, header, sender); // [func][req]
sessionPtrs->req = ILibDuktape_InitReadableStream(serverPtrs->ctx, NULL, NULL, NULL);
ILibDuktape_readableStream_SetPauseResumeHandlers(sessionPtrs->req, ILibDuktape_http_server_Pause, ILibDuktape_http_server_Resume, sender);
duk_push_heap_stash(serverPtrs->ctx); // [func][req][stash]
duk_dup(serverPtrs->ctx, -2); // [func][req][stash][req]
duk_put_prop_string(serverPtrs->ctx, -2, Duktape_GetStashKey(sessionPtrs->req->object)); // [func][req][stash]
duk_pop(serverPtrs->ctx); // [func][req]
sessionPtrs->res = ILibDuktape_http_server_PUSH_ServerResponse(serverPtrs->ctx, sender); // [func][req][res]
if (duk_pcall(serverPtrs->ctx, 2) != 0) // [retVal]
{
ILibDuktape_Process_UncaughtException(serverPtrs->ctx);
}
duk_pop(serverPtrs->ctx); // ...
}
// Now we just write to the ReadableStream
if (sessionPtrs->req != NULL)
{
if (endPointer > 0)
{
r += ILibDuktape_readableStream_WriteData(sessionPtrs->req, bodyBuffer + *beginPointer, endPointer - *beginPointer);
}
if (done == ILibWebServer_DoneFlag_Done && r == 0)
{
r += ILibDuktape_readableStream_WriteEnd(sessionPtrs->req);
}
if (done == ILibWebServer_DoneFlag_Done && r == 0)
{
duk_push_heap_stash(serverPtrs->ctx); // [stash]
duk_del_prop_string(serverPtrs->ctx, -1, Duktape_GetStashKey(sessionPtrs->req->object));
duk_pop(serverPtrs->ctx); // ...
}
}
if (r == 0) { *beginPointer = endPointer; }
if (done == ILibWebServer_DoneFlag_Done && sender->OnReceive == ILibDuktape_http_server_OnReceive) { sessionPtrs->req = NULL; }
}
void ILibDuktape_http_server_OnSendOK(ILibWebServer_Session *sender)
{
ILibDuktape_http_session_ptrs *sessionPtrs = (ILibDuktape_http_session_ptrs*)sender->Reserved_Transport.ChainLink.ExtraMemoryPtr;
ILibDuktape_WritableStream_Ready(sessionPtrs->res);
}
void ILibDuktape_http_server_OnSession(ILibWebServer_Session *session, void *user)
{
session->OnReceive = ILibDuktape_http_server_OnReceive;
session->OnSendOK = ILibDuktape_http_server_OnSendOK;
}
#ifndef MICROSTACK_NOTLS
int ILibDuktape_http_server_clientVerify(ILibWebServer_ServerToken sender, int preverify_ok, STACK_OF(X509) *certs, struct sockaddr_in6* address)
{
int i;
int retVal = 1;
ILibDuktape_http_server_ptrs *ptrs = (ILibDuktape_http_server_ptrs*)((ILibChain_Link*)sender)->ExtraMemoryPtr;
void *OnVerify;
int rejectUnauthorized = 0;
char addr[512];
duk_push_heapptr(ptrs->ctx, ptrs->serverObject); // [server]
duk_get_prop_string(ptrs->ctx, -1, TLSPTR); // [server][tlsSettings]
OnVerify = Duktape_GetHeapptrProperty(ptrs->ctx, -1, "checkClientIdentity");
rejectUnauthorized = Duktape_GetBooleanProperty(ptrs->ctx, -1, "rejectUnauthorized ", 0);
duk_pop_2(ptrs->ctx); // ...
if (rejectUnauthorized != 0 && preverify_ok == 0) { retVal = 0; }
if (OnVerify != NULL)
{
duk_push_heapptr(ptrs->ctx, OnVerify); // [func]
duk_push_heapptr(ptrs->ctx, ptrs->serverObject); // [func][this]
ILibInet_ntop2((struct sockaddr*)address, addr, sizeof(addr));
duk_push_string(ptrs->ctx, addr); // [func][this][server]
duk_push_array(ptrs->ctx); // [func][this][server][certs]
for (i = 0; i < sk_X509_num(certs); ++i)
{
ILibDuktape_X509_PUSH(ptrs->ctx, sk_X509_value(certs, i)); // [func][this][server][certs][cert]
duk_put_prop_index(ptrs->ctx, -2, i); // [func][this][server][certs]
}
if (duk_pcall_method(ptrs->ctx, 2) != 0) { retVal = 0; } // [retVal]
duk_pop(ptrs->ctx); // ...
}
return retVal;
}
#endif
duk_ret_t ILibDuktape_http_server_listen(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
int port = duk_require_int(ctx, 0);
//char *host = nargs > 1 ? ((char*)duk_require_string(ctx, 1)) : NULL;
int maxConnections = nargs > 2 ? duk_require_int(ctx, 2) : 5;
void *chain;
ILibWebServer_ServerToken server;
char *key;
ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_GetEmitter_fromThis(ctx);
duk_push_this(ctx); // [server]
duk_get_prop_string(ctx, -1, "chain"); // [server][chain]
chain = duk_to_pointer(ctx, -1);
duk_pop(ctx); // [server]
server = ILibWebServer_Create2(chain, maxConnections, port, ILibDuktape_http_server_OnSession, sizeof(ILibDuktape_http_server_ptrs), NULL);
((ILibDuktape_http_server_ptrs*)((ILibChain_Link*)server)->ExtraMemoryPtr)->ctx = ctx;
((ILibDuktape_http_server_ptrs*)((ILibChain_Link*)server)->ExtraMemoryPtr)->serverObject = duk_get_heapptr(ctx, -1);
ILibDuktape_EventEmitter_AddEventHeapptr(emitter, "request", &(((ILibDuktape_http_server_ptrs*)((ILibChain_Link*)server)->ExtraMemoryPtr)->OnResponse));
ILibDuktape_EventEmitter_AddEventHeapptr(emitter, "connect", &(((ILibDuktape_http_server_ptrs*)((ILibChain_Link*)server)->ExtraMemoryPtr)->OnConnect));
ILibDuktape_EventEmitter_AddEventHeapptr(emitter, "upgrade", &(((ILibDuktape_http_server_ptrs*)((ILibChain_Link*)server)->ExtraMemoryPtr)->OnUpgrade));
#ifndef MICROSTACK_NOTLS
if (duk_has_prop_string(ctx, -1, TLSPTR))
{
struct util_cert *cert;
struct util_cert *nonleaf = NULL;
duk_get_prop_string(ctx, -1, TLSPTR); // [server][tlsSettings]
duk_get_prop_string(ctx, -1, TLS_CERT); // [server][tlsSettings][cert]
cert = (struct util_cert*)Duktape_GetBuffer(ctx, -1, NULL);
duk_pop(ctx); // [server][tlsSettings]
if (duk_has_prop_string(ctx, -1, TLS_CERT_NON_LEAF))
{
duk_get_prop_string(ctx, -1, TLS_CERT_NON_LEAF); // [server][tlsSettings][nonleaf]
nonleaf = (struct util_cert*)Duktape_GetBuffer(ctx, -1, NULL);
duk_pop(ctx); // [server][tlsSettings]
}
ILibWebServer_EnableHTTPS(server, cert, nonleaf != NULL ? nonleaf->x509 : NULL, Duktape_GetBooleanProperty(ctx, -1, "requestCert", 0), ILibDuktape_http_server_clientVerify);
duk_pop(ctx); // [server]
}
#endif
duk_push_pointer(ctx, server); // [server][serverPtr]
duk_put_prop_string(ctx, -2, "ServerPtr"); // [server]
key = Duktape_GetStashKey(server);
duk_push_heap_stash(ctx); // [server][stash]
duk_swap_top(ctx, -2); // [stash][server]
duk_put_prop_string(ctx, -2, key); // [stash]
return 0;
}
duk_ret_t ILibDuktape_http_server_tlsSettings_Finalizer(duk_context *ctx)
{
return 0;
}
duk_ret_t ILibDuktape_http_createServer(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
int i;
ILibDuktape_EventEmitter *emitter;
duk_push_this(ctx); // [http]
duk_get_prop_string(ctx, -1, "chain"); // [http][chain]
duk_push_object(ctx); // [http][chain][server]
duk_swap_top(ctx, -2); // [http][server][chain]
duk_put_prop_string(ctx, -2, "chain"); // [http][server]
emitter = ILibDuktape_EventEmitter_Create(ctx);
ILibDuktape_EventEmitter_CreateEventEx(emitter, "request");
ILibDuktape_EventEmitter_CreateEventEx(emitter, "listening");
ILibDuktape_EventEmitter_CreateEventEx(emitter, "upgrade");
ILibDuktape_EventEmitter_CreateEventEx(emitter, "connect");
for (i = 0; i < nargs; ++i)
{
if (duk_is_function(ctx, i))
{
// requestListener
ILibDuktape_EventEmitter_AddOn(emitter, "request", duk_require_heapptr(ctx, i));
}
else if (duk_is_object(ctx, i))
{
// options
if (duk_has_prop_string(ctx, 0, "request"))
{
ILibDuktape_EventEmitter_AddOn(emitter, "request", Duktape_GetHeapptrProperty(ctx, 0, "request"));
}
else if (duk_has_prop_string(ctx, i, "MeshAgent"))
{
#ifndef MICROSTACK_NOTLS
struct util_cert *cert;
struct util_cert *nonleaf;
duk_get_prop_string(ctx, i, "MeshAgent"); // [http][server][MeshAgent]
duk_get_prop_string(ctx, -1, ILibDuktape_MeshAgent_Cert_Server); // [http][server][MeshAgent][cert]
cert = (struct util_cert*)duk_get_pointer(ctx, -1);
duk_get_prop_string(ctx, -2, ILibDuktape_MeshAgent_Cert_NonLeaf); // [http][server][MeshAgent][cert][nonleaf]
nonleaf = (struct util_cert*)duk_get_pointer(ctx, -1);
duk_pop_3(ctx); // [http][server]
duk_push_object(ctx); // [http][server][tlsSettings]
duk_push_external_buffer(ctx); // [http][server][tlsSettings][cert]
duk_config_buffer(ctx, -1, cert, sizeof(struct util_cert));
duk_put_prop_string(ctx, -2, TLS_CERT); // [http][server][tlsSettings]
duk_push_external_buffer(ctx); // [http][server][tlsSettings][nonleaf]
duk_config_buffer(ctx, -1, nonleaf, sizeof(struct util_cert));
duk_put_prop_string(ctx, -2, TLS_CERT_NON_LEAF); // [http][server][tlsSettings]
duk_put_prop_string(ctx, -2, TLSPTR); // [http][server]
#else
return(ILibDuktape_Error(ctx, "createServer(): Invalid Argument. MeshAgent only valid with TLS support"));
#endif
}
#ifndef MICROSTACK_NOTLS
else if (duk_has_prop_string(ctx, i, "pfx") && duk_has_prop_string(ctx, i, "passphrase"))
{
struct util_cert *cert;
char *pfx;
duk_size_t pfxLen;
char *passphrase;
duk_get_prop_string(ctx, i, "pfx"); // [http][server][pfx]
pfx = Duktape_GetBuffer(ctx, -1, &pfxLen);
duk_pop(ctx); // [http][server]
duk_get_prop_string(ctx, i, "passphrase"); // [http][server][passphrase]
passphrase = Duktape_GetBuffer(ctx, -1, NULL);
duk_pop(ctx); // [http][server]
duk_push_object(ctx); // [http][server][tlsSettings]
duk_push_fixed_buffer(ctx, sizeof(struct util_cert)); // [http][server][tlsSettings][cert]
cert = (struct util_cert*)Duktape_GetBuffer(ctx, -1, NULL);
duk_put_prop_string(ctx, -2, TLS_CERT); // [http][server][tlsSettings]
if (util_from_p12(pfx, (int)pfxLen, passphrase, cert) == 0) { duk_push_string(ctx, "ERROR reading certificate"); duk_throw(ctx); return(DUK_RET_ERROR); }
else
{
ILibDuktape_CreateFinalizer(ctx, ILibDuktape_http_server_tlsSettings_Finalizer);
duk_put_prop_string(ctx, -2, TLSPTR); // [http][server]
}
}
#endif
if (duk_has_prop_string(ctx, -1, TLSPTR)) // [http][server]
{
duk_get_prop_string(ctx, -1, TLSPTR); // [http][server][tlsSettings]
duk_push_boolean(ctx, (duk_bool_t)Duktape_GetBooleanProperty(ctx, i, "requestCert", 0)); // [http][server][tlsSettings][requestCert]
duk_put_prop_string(ctx, -2, "requestCert"); // [http][server][tlsSettings]
duk_push_boolean(ctx, (duk_bool_t)Duktape_GetBooleanProperty(ctx, i, "rejectUnauthorized", 0)); // [http][server][tlsSettings][reject]
duk_put_prop_string(ctx, -2, "rejectUnauthorized"); // [http][server][tlsSettings]
duk_push_heapptr(ctx, Duktape_GetHeapptrProperty(ctx, i, "checkClientIdentity")); // [http][server][tlsSettings][checkClient]
duk_put_prop_string(ctx, -2, "checkClientIdentity"); // [http][server][tlsSettings]
duk_put_prop_string(ctx, -2, TLSPTR); // [http][server]
}
}
}
duk_push_c_function(ctx, ILibDuktape_http_server_listen, DUK_VARARGS); // [http][server][func]
duk_put_prop_string(ctx, -2, "listen"); // [http][server]
return 1;
}
void ILibDuktape_http_request_Pause(ILibDuktape_readableStream* sender, void *user)
{
ILibWebClient_Pause(user);
}
void ILibDuktape_http_request_Resume(ILibDuktape_readableStream* sender, void *user)
{
ILibWebClient_Resume(user);
}
void ILibDuktape_http_request_OnResponse(ILibWebClient_StateObject WebStateObject, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebClient_ReceiveStatus recvStatus, void *user1, void *user2, int *PAUSE)
{
int r = 0;
duk_context *ctx = (duk_context*)user1;
ILibDuktape_http_requestClient_callbacks *ptrs = (ILibDuktape_http_requestClient_callbacks*)user2;
if (ptrs == NULL) { return; }
if(ptrs->readableStream != NULL)
{
if (endPointer > 0)
{
r += ILibDuktape_readableStream_WriteData(ptrs->readableStream, bodyBuffer + *beginPointer, endPointer - *beginPointer);
}
if(r == 0 && recvStatus == ILibWebClient_ReceiveStatus_Complete)
{
r += ILibDuktape_readableStream_WriteEnd(ptrs->readableStream);
}
}
else
{
if (InterruptFlag != 0 || header == NULL)
{
if (ctx != NULL)
{
duk_push_heapptr(ctx, ptrs->OnReceive); // [func]
duk_push_heapptr(ctx, ptrs->clientRequest); // [func][this]
duk_del_prop_string(ctx, -1, HTTP_REQUEST_TOKEN_PTR); // (Prevents crash in Request Finalizer)
duk_push_null(ctx); // [func][this][null]
if (duk_pcall_method(ctx, 1) != 0) // [retVal]
{
ILibDuktape_Process_UncaughtException(ctx);
}
duk_pop(ctx); // ...
}
return;
}
ILibDuktape_http_server_PUSH_IncomingMessage(ctx, header, NULL); // [iMsg]
duk_push_pointer(ctx, WebStateObject);
duk_put_prop_string(ctx, -2, HTTP_INCOMINGMSG_WebStateObject);
ptrs->readableStream = ILibDuktape_InitReadableStream(ctx, NULL, NULL, NULL);
ILibDuktape_readableStream_SetPauseResumeHandlers(ptrs->readableStream, ILibDuktape_http_request_Pause, ILibDuktape_http_request_Resume, WebStateObject);
duk_push_heapptr(ctx, ptrs->clientRequest); // [iMsg][clientRequest]
duk_swap_top(ctx, -2); // [clientRequest][iMsg]
duk_dup(ctx, -1); // [clientRequest][iMsg][iMsg]
duk_put_prop_string(ctx, -3, "_iMsgPtr"); // [clientRequest][iMsg]
duk_swap_top(ctx, -2); // [iMsg][clientRequest]
duk_push_heapptr(ctx, ptrs->OnReceive); // [iMsg][clientRequest][func]
duk_swap(ctx, -3, -1); // [func][clientRequest/this][iMsg]
if (duk_pcall_method(ctx, 1) != 0) // [retVal]
{
ILibDuktape_Process_UncaughtException(ctx);
}
duk_pop(ctx); // ...
if (endPointer > 0) { r += ILibDuktape_readableStream_WriteData(ptrs->readableStream, bodyBuffer + *beginPointer, endPointer - *beginPointer); }
if (r == 0 && recvStatus == ILibWebClient_ReceiveStatus_Complete) { r += ILibDuktape_readableStream_WriteEnd(ptrs->readableStream); }
}
if (r == 0) { *beginPointer = endPointer; }
if (recvStatus == ILibWebClient_ReceiveStatus_Complete)
{
duk_push_heapptr(ctx, ptrs->clientRequest); // [clientRequest]
duk_del_prop_string(ctx, -1, HTTP_REQUEST_TOKEN_PTR);
ILibDuktape_EventEmitter_RemoveAll(ILibDuktape_EventEmitter_GetEmitter(ctx, -1));
duk_pop(ctx); // ...
}
}
ILibTransport_DoneState ILibDuktape_http_WebSocket_socket_write(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user)
{
ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user;
if (ptrs->wcdo == NULL)
{
return(ILibTransport_DoneState_ERROR);
}
return((ILibTransport_DoneState)ILibWebClient_WebSocket_Send(ptrs->wcdo, ILibWebClient_WebSocket_DataType_BINARY, buffer, bufferLen, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete));
}
void ILibDuktape_http_WebSocket_socket_end(ILibDuktape_DuplexStream *stream, void *user)
{
ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user;
ILibWebClient_StateObject wcdo = ptrs->wcdo;
ptrs->wcdo = NULL;
if (wcdo != NULL) { ILibWebClient_Disconnect(wcdo); }
}
void ILibDuktape_http_WebSocket_socket_pause(ILibDuktape_DuplexStream *sender, void *user)
{
ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user;
ILibWebClient_Pause(ptrs->wcdo);
}
void ILibDuktape_http_WebSocket_socket_resume(ILibDuktape_DuplexStream *sender, void *user)
{
ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user;
ILibWebClient_Resume(ptrs->wcdo);
}
duk_ret_t ILibDuktape_http_WebSocket_socket_finalizer(duk_context *ctx)
{
ILibDuktape_WebSocket_Pointers *ptrs;
if (duk_has_prop_string(ctx, 0, HTTP_SOCKET_PTRS))
{
duk_get_prop_string(ctx, 0, HTTP_SOCKET_PTRS);
ptrs = (ILibDuktape_WebSocket_Pointers*)duk_get_pointer(ctx, -1);
ILibWebClient_Disconnect(ptrs->wcdo);
}
return 0;
}
void ILibDuktape_http_WebSocket_timeoutSink(ILibWebClient_StateObject state, void *user)
{
ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user;
if (ptrs->onTimeout != NULL)
{
duk_push_heapptr(ptrs->ctx, ptrs->onTimeout); // [func]
duk_push_heapptr(ptrs->ctx, ptrs->socket_ptr); // [func][this]
if (duk_pcall_method(ptrs->ctx, 0) != 0) { ILibDuktape_Process_UncaughtException(ptrs->ctx); }
duk_pop(ptrs->ctx);
}
if (ptrs->timeout > 0) { ILibWebClient_SetTimeout(ptrs->wcdo, ptrs->timeout, ILibDuktape_http_WebSocket_timeoutSink, ptrs); }
}
duk_ret_t ILibDuktape_http_WebSocket_setTimeout(duk_context *ctx)
{
ILibDuktape_EventEmitter *emitter;
ILibDuktape_WebSocket_Pointers *ptrs;
int nargs = duk_get_top(ctx);
int milliseconds = duk_require_int(ctx, 0);
duk_push_this(ctx); // [socket]
duk_get_prop_string(ctx, -1, HTTP_SOCKET_PTRS); // [socket][ptrs]
ptrs = (ILibDuktape_WebSocket_Pointers*)duk_get_pointer(ctx, -1);
if (milliseconds < 1000) { return(ILibDuktape_Error(ctx, "http/net.socket.setTimeout(): Error, timeout cannot be less than 1 second")); }
ILibWebClient_SetTimeout(ptrs->wcdo, milliseconds / 1000, ILibDuktape_http_WebSocket_timeoutSink, ptrs);
ptrs->timeout = milliseconds / 1000;
if (nargs > 1)
{
emitter = ILibDuktape_EventEmitter_GetEmitter_fromThis(ctx);
ILibDuktape_EventEmitter_AddOnce(emitter, "timeout", duk_require_heapptr(ctx, 1));
}
return 0;
}
duk_ret_t ILibDuktape_http_WebSocket_ping(duk_context *ctx)
{
ILibDuktape_WebSocket_Pointers *ptrs;
duk_push_this(ctx); // [socket]
duk_get_prop_string(ctx, -1, HTTP_SOCKET_PTRS); // [socket][ptrs]
ptrs = (ILibDuktape_WebSocket_Pointers*)duk_get_pointer(ctx, -1);
ILibWebClient_WebSocket_Ping(ptrs->wcdo);
return 0;
}
duk_ret_t ILibDuktape_http_WebSocket_pong(duk_context *ctx)
{
ILibDuktape_WebSocket_Pointers *ptrs;
duk_push_this(ctx); // [socket]
duk_get_prop_string(ctx, -1, HTTP_SOCKET_PTRS); // [socket][ptrs]
ptrs = (ILibDuktape_WebSocket_Pointers*)duk_get_pointer(ctx, -1);
ILibWebClient_WebSocket_Pong(ptrs->wcdo);
return 0;
}
ILibWebClient_WebSocket_PingResponse ILibDuktape_http_WebSocket_pingHandler(ILibWebClient_StateObject state, void *user)
{
ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user;
ILibWebClient_WebSocket_PingResponse retVal = ILibWebClient_WebSocket_PingResponse_Respond;
if (ptrs->onPing != NULL)
{
duk_push_heapptr(ptrs->ctx, ptrs->onPing); // [func]
duk_push_heapptr(ptrs->ctx, ptrs->socket_ptr); // [func][this]
if (duk_pcall_method(ptrs->ctx, 0) != 0) { retVal = ILibWebClient_WebSocket_PingResponse_None; }
duk_pop(ptrs->ctx); // ...
}
return retVal;
}
void ILibDuktape_http_WebSocket_pongHandler(ILibWebClient_StateObject state, void *user)
{
ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user;
if (ptrs->onPong != NULL)
{
duk_push_heapptr(ptrs->ctx, ptrs->onPong); // [func]
duk_push_heapptr(ptrs->ctx, ptrs->socket_ptr); // [func][this]
if (duk_pcall_method(ptrs->ctx, 0) != 0) { ILibDuktape_Process_UncaughtException(ptrs->ctx); }
duk_pop(ptrs->ctx); // ...
}
}
void ILibDuktape_http_WebSocket_PUSH_socket(duk_context *ctx, ILibWebClient_StateObject *wcdo, ILibDuktape_WebSocket_Pointers* ptrs)
{
ILibDuktape_EventEmitter *emitter;
if (ptrs->socket_ptr == NULL)
{
duk_push_object(ctx); // [socket]
duk_push_pointer(ctx, ptrs); // [socket][ptr]
duk_put_prop_string(ctx, -2, HTTP_SOCKET_PTRS); // [socket]
ptrs->socket_ptr = duk_get_heapptr(ctx, -1);
ptrs->stream = ILibDuktape_DuplexStream_Init(ctx, ILibDuktape_http_WebSocket_socket_write, ILibDuktape_http_WebSocket_socket_end, ILibDuktape_http_WebSocket_socket_pause, ILibDuktape_http_WebSocket_socket_resume, ptrs);
ptrs->wcdo = wcdo;
ILibDuktape_CreateFinalizer(ctx, ILibDuktape_http_WebSocket_socket_finalizer);
emitter = ILibDuktape_EventEmitter_Create(ctx);
ILibDuktape_EventEmitter_CreateEvent(emitter, "timeout", &(ptrs->onTimeout));
ILibDuktape_EventEmitter_CreateEvent(emitter, "ping", &(ptrs->onPing));
ILibDuktape_EventEmitter_CreateEvent(emitter, "pong", &(ptrs->onPong));
ILibDuktape_CreateProperty_InstanceMethod(ctx, "ping", ILibDuktape_http_WebSocket_ping, DUK_VARARGS);
ILibDuktape_CreateProperty_InstanceMethod(ctx, "pong", ILibDuktape_http_WebSocket_pong, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "setTimeout", ILibDuktape_http_WebSocket_setTimeout, DUK_VARARGS);
ILibWebClient_WebSocket_SetPingPongHandler(ptrs->wcdo, ILibDuktape_http_WebSocket_pingHandler, ILibDuktape_http_WebSocket_pongHandler, ptrs);
}
else
{
duk_push_heapptr(ctx, ptrs->socket_ptr); // [socket]
}
}
void ILibDuktape_http_request_WebSocket_OnResponse(ILibWebClient_StateObject WebStateObject, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebClient_ReceiveStatus recvStatus, void *user1, void *user2, int *PAUSE)
{
duk_context *ctx = (duk_context*)user1;
ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user2;
if (ctx == NULL || ptrs == NULL) { return; }
if (header != NULL && header->StatusCode != 101)
{
duk_push_heapptr(ctx, ptrs->clientRequest_ptr); // [clientRequest]
duk_get_prop_string(ctx, -1, "emit"); // [clientRequest][emit]
duk_swap_top(ctx, -2); // [emit][this]
duk_push_string(ctx, "response"); // [emit][this][response]
ILibDuktape_http_server_PUSH_IncomingMessage(ctx, header, NULL); // [emit][this][response][iMsg]
duk_push_pointer(ctx, WebStateObject);
duk_put_prop_string(ctx, -2, HTTP_INCOMINGMSG_WebStateObject); // [emit][this][response][iMsg]
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http_request_WebSocket_OnResponse(): Error dispatching 'response' "); }
duk_pop(ctx); // ...
*beginPointer = endPointer;
return;
}
if (header != NULL && header->StatusCode == 101)
{
switch (recvStatus)
{
case ILibWebClient_ReceiveStatus_Connection_Established:
duk_push_heapptr(ctx, ptrs->clientRequest_ptr); // [clientRequest]
duk_get_prop_string(ctx, -1, "emit"); // [clientRequest][emit]
duk_swap_top(ctx, -2); // [emit][this]
duk_push_string(ctx, "upgrade"); // [emit][this][upgrade]
ILibDuktape_http_WebSocket_PUSH_socket(ctx, WebStateObject, ptrs); // [emit][this][upgrade][socket]
duk_dup(ctx, -1); // [emit][this][upgrade][socket][socket]
duk_put_prop_string(ctx, -4, "\xFF_socket"); // [emit][this][upgrade][socket]
ILibDuktape_http_server_PUSH_IncomingMessage(ctx, header, NULL); // [emit][this][upgrade][socket][msg]
duk_swap_top(ctx, -2); // [emit][this][upgrade][msg][socket]
duk_push_null(ctx); // [emit][this][upgrade][msg][socket][head]
if (duk_pcall_method(ctx, 4) != 0) // [retVal]
{
ILibDuktape_Process_UncaughtException(ctx);
}
duk_pop(ctx); // ...
*beginPointer = endPointer;
break;
case ILibWebClient_ReceiveStatus_MoreDataToBeReceived:
if (ptrs->socket_ptr != NULL && ptrs->stream != NULL)
{
ILibDuktape_DuplexStream_WriteData(ptrs->stream, bodyBuffer, endPointer);
}
*beginPointer = endPointer;
break;
default:
// ToDo: See if we need to handle Partial/LastPartial
break;
}
}
if (recvStatus == ILibWebClient_ReceiveStatus_Complete)
{
if (ptrs->socket_ptr != NULL && ptrs->stream != NULL)
{
ILibDuktape_DuplexStream_WriteEnd(ptrs->stream);
}
duk_push_heapptr(ctx, ptrs->clientRequest_ptr); //[clientRequest]
if (header == NULL && ptrs->stream == NULL)
{
duk_get_prop_string(ctx, -1, "emit"); //[clientRequest][emit]
duk_dup(ctx, -2); //[clientRequest][emit][this]
duk_push_string(ctx, "error"); //[clientRequest][emit][this][error]
duk_push_string(ctx, "WebSocket Connection Error"); //[clientRequest][emit][this][error][msg]
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "Error: http.clientRequet.onError()"); }
duk_pop(ctx); //[clientRequest]
}
duk_del_prop_string(ctx, -1, HTTP_REQUEST_TOKEN_PTR);
duk_pop(ctx);
if (ptrs->socket_ptr != NULL)
{
duk_push_heapptr(ctx, ptrs->socket_ptr); //[socket]
duk_del_prop_string(ctx, -1, HTTP_SOCKET_PTRS);
ILibDuktape_EventEmitter_RemoveAll(ILibDuktape_EventEmitter_GetEmitter(ctx, -1));
duk_pop(ctx);
}
if (ptrs->clientRequest_ptr != NULL)
{
duk_push_heapptr(ctx, ptrs->clientRequest_ptr);
ILibDuktape_EventEmitter_RemoveAll(ILibDuktape_EventEmitter_GetEmitter(ctx, -1));
duk_pop(ctx);
}
}
}
void ILibDuktape_http_request_OnSendOK(ILibWebClient_StateObject sender, void *user1, void *user2)
{
//duk_context *ctx = (duk_context*)user1;
ILibDuktape_http_requestClient_callbacks *ptrs = (ILibDuktape_http_requestClient_callbacks*)user2;
if (ptrs->requestStream != NULL)
{
ILibDuktape_WritableStream_Ready(ptrs->requestStream);
}
}
#ifndef MICROSTACK_NOTLS
int ILibDuktape_http_request_tls_verify(ILibWebClient_RequestToken sender, int preverify_ok, STACK_OF(X509) *certs, struct sockaddr_in6 *address)
{
int i;
char addr[512];
int retVal = 0;
void **user = (void**)ILibWebClient_RequestToken_GetUserObjects(sender);
duk_context *ctx = (duk_context*)user[0];
void *clientRequest = NULL;
void *checkServerIdentity = NULL;
int rejectUnauthorised = 0;
if(((ILibDuktape_http_request_dataType*)user[1])->STRUCT_TYPE == ILibDuktape_http_request_dataType_request)
{
ILibDuktape_http_requestClient_callbacks* ptrs = (ILibDuktape_http_requestClient_callbacks*)user[1];
clientRequest = ptrs->clientRequest;
checkServerIdentity = ptrs->checkServerIdentity;
rejectUnauthorised = ptrs->rejectUnauthorized;
}
else
{
ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user[1];
clientRequest = ptrs->clientRequest_ptr;
checkServerIdentity = ptrs->checkServerIdentity;
rejectUnauthorised = ptrs->rejectUnauthorized;
}
if (rejectUnauthorised != 0 && preverify_ok == 0) { return 0; }
if (checkServerIdentity != NULL)
{
duk_push_heapptr(ctx, checkServerIdentity); // [func]
duk_push_heapptr(ctx, clientRequest); // [func][this]
ILibInet_ntop2((struct sockaddr*)address, addr, sizeof(addr));
duk_push_string(ctx, addr); // [func][this][server]
duk_push_array(ctx); // [func][this][server][certs]
for (i = 0; i < sk_X509_num(certs); ++i)
{
ILibDuktape_X509_PUSH(ctx, sk_X509_value(certs, i)); // [func][this][server][certs][cert]
duk_put_prop_index(ctx, -2, i); // [func][this][server][certs]
}
if (duk_pcall_method(ctx, 2) == 0) { retVal = 1; } // [retVal]
duk_pop(ctx); // ...
}
else
{
retVal = 1;
}
return retVal;
}
#endif
void ILibDuktape_http_webSocket_onSendOk(ILibWebClient_StateObject sender, void *user1, void *user2)
{
ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user2;
if (ptrs != NULL && ptrs->ctx != NULL && ptrs->stream != NULL)
{
ILibDuktape_DuplexStream_Ready(ptrs->stream);
}
}
duk_ret_t ILibDuktape_http_request(duk_context *ctx)
{
ILibHTTPPacket *packet;
char *host;
duk_size_t hostLen;
char *key;
int nargs = duk_get_top(ctx);
if (nargs < 1) { duk_push_string(ctx, "Too Few Arguments"); duk_throw(ctx); return(DUK_RET_ERROR); }
int isWebSocket = 0;
ILibWebClient_RequestManager wcm;
struct sockaddr_in6 dest;
ILibWebClient_RequestToken token;
ILibDuktape_globalTunnel_data *globalTunnel = ILibDuktape_GetGlobalTunnel(ctx);
char *path, *method;
duk_size_t pathLen, methodLen;
char *proto = Duktape_GetStringPropertyValue(ctx, 0, "protocol", ILibDuktape_http_getDefaultProto(ctx));
#ifndef MICROSTACK_NOTLS
ILibWebClient_RequestToken_HTTPS protocol = (strncmp(proto, "https:", 6) == 0 || strncmp(proto, "wss:", 4) == 0) ? ILibWebClient_RequestToken_USE_HTTPS : ILibWebClient_RequestToken_USE_HTTP;
#else
ILibWebClient_RequestToken_HTTPS protocol = ILibWebClient_RequestToken_USE_HTTP;
#endif
if (strncmp(proto, "wss:", 4) == 0 || strncmp(proto, "ws:", 3) == 0) { isWebSocket = 1; }
duk_push_this(ctx);
if (duk_has_prop_string(ctx, -1, HTTP_WEBCLIENT_MGR))
{
duk_get_prop_string(ctx, -1, HTTP_WEBCLIENT_MGR);
wcm = (ILibWebClient_RequestManager)duk_to_pointer(ctx, -1);
}
else
{
duk_get_prop_string(ctx, -1, "chain"); // [http][chain]
duk_get_prop_string(ctx, -2, "RequestPoolSize"); // [http][chain][poolSize]
wcm = ILibCreateWebClient(duk_to_int(ctx, -1), duk_to_pointer(ctx, -2));
duk_pop_2(ctx); // [http]
#ifndef MICROSTACK_NOTLS
{
char *pfx;
duk_size_t pfxLen;
char *passphrase = Duktape_GetStringPropertyValue(ctx, 0, "passphrase", "");
if (duk_has_prop_string(ctx, 0, "pfx") && protocol == ILibWebClient_RequestToken_USE_HTTPS)
{
struct util_cert cert;
duk_get_prop_string(ctx, 0, "pfx"); // [http][pfx]
pfx = Duktape_GetBuffer(ctx, -1, &pfxLen);
duk_pop(ctx); // [http]
if (util_from_p12(pfx, (int)pfxLen, passphrase, &cert) != 0)
{
duk_push_pointer(ctx, cert.pkey); // [http][pkey]
duk_put_prop_string(ctx, -2, "\xFF_pkey"); // [http]
duk_push_pointer(ctx, cert.x509); // [http][x509]
duk_put_prop_string(ctx, -2, "\xFF_x509"); // [http]
ILibWebClient_EnableHTTPS(wcm, &cert, NULL, ILibDuktape_http_request_tls_verify);
}
}
else if (duk_has_prop_string(ctx, 0, "MeshAgent") && protocol == ILibWebClient_RequestToken_USE_HTTPS)
{
duk_get_prop_string(ctx, 0, "MeshAgent"); // [http][MeshAgent]
duk_get_prop_string(ctx, -1, ILibDuktape_MeshAgent_Cert_Client); // [http][MeshAgent][clientCert]
duk_get_prop_string(ctx, -2, ILibDuktape_MeshAgent_Cert_NonLeaf); // [http][MeshAgent][clientCert][nonLeafCert]
ILibWebClient_EnableHTTPS(wcm, (struct util_cert*)duk_get_pointer(ctx, -2), ((struct util_cert*)duk_get_pointer(ctx, -1))->x509, ILibDuktape_http_request_tls_verify);
duk_pop_3(ctx); // [http]
}
else if (protocol == ILibWebClient_RequestToken_USE_HTTPS)
{
ILibWebClient_EnableHTTPS(wcm, NULL, NULL, ILibDuktape_http_request_tls_verify);
}
}
#endif
duk_push_pointer(ctx, wcm); // [http][wcm]
duk_put_prop_string(ctx, -2, HTTP_WEBCLIENT_MGR); // [http]
}
if (duk_has_prop_string(ctx, 0, "hostname"))
{
host = Duktape_GetStringPropertyValueEx(ctx, 0, "hostname", "127.0.0.1", &hostLen);
}
else
{
host = Duktape_GetStringPropertyValueEx(ctx, 0, "host", "127.0.0.1", &hostLen);
}
if (duk_has_prop_string(ctx, 0, "proxy"))
{
duk_get_prop_string(ctx, 0, "proxy");
globalTunnel = (ILibDuktape_globalTunnel_data*)ILibScratchPad;
memset(globalTunnel, 0, sizeof(ILibDuktape_globalTunnel_data));
ILibResolveEx(Duktape_GetStringPropertyValueEx(ctx, -1, "host", "127.0.0.1", NULL), (unsigned short)Duktape_GetIntPropertyValue(ctx, -1, "port", 8080), &(globalTunnel->proxyServer));
if (globalTunnel->proxyServer.sin6_family == AF_UNSPEC) { return(ILibDuktape_Error(ctx, "http.get(): Cannot resolve proxy host %s", Duktape_GetStringPropertyValueEx(ctx, -1, "host", "127.0.0.1", NULL))); }
}
else if (duk_has_prop_string(ctx, 0, "noProxy")) { globalTunnel = NULL; }
packet = ILibCreateEmptyPacket();
ILibSetVersion(packet, "1.1", 3);
method = Duktape_GetStringPropertyValueEx(ctx, 0, "method", "GET", &methodLen);
path = Duktape_GetStringPropertyValueEx(ctx, 0, "path", "/", &pathLen);
ILibSetDirective(packet, method, (int)methodLen, path, (int)pathLen);
if (isWebSocket != 0)
{
union { int i; void*p; }u;
int len;
char value[32];
char nonce[16];
char *enc = value;
util_random(16, nonce);
len = ILibBase64Encode((unsigned char*)nonce, 16, (unsigned char**)&enc);
enc[len] = 0;
u.i = Duktape_GetIntPropertyValue(ctx, 0, "webSocketBufferSize", 65535);
ILibAddHeaderLine(packet, "Upgrade", -1, "websocket", -1);
ILibAddHeaderLine(packet, "Connection", -1, "Upgrade", -1);
ILibAddHeaderLine(packet, "Sec-WebSocket-Key", -1, enc, -1);
ILibAddHeaderLine(packet, "Sec-WebSocket-Version", -1, "13", -1);
ILibHTTPPacket_Stash_Put(packet, "_WebSocketBufferSize", -1, u.p);
ILibHTTPPacket_Stash_Put(packet, "_WebSocketOnSendOK", -1, ILibDuktape_http_webSocket_onSendOk);
}
if (duk_has_prop_string(ctx, 0, "headers"))
{
duk_get_prop_string(ctx, 0, "headers");
duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY);
while (duk_next(ctx, -1, 1))
{
key = (char*)duk_to_string(ctx, -2);
ILibAddHeaderLine(packet, key, -1, (char*)duk_to_string(ctx, -1), -1);
duk_pop_2(ctx);
}
}
if (ILibGetHeaderLine(packet, "host", 4) == NULL)
{
ILibAddHeaderLine(packet, "host", 4, host, (int)hostLen);
}
memset(&dest, 0, sizeof(struct sockaddr_in6));
ILibResolveEx(host, (unsigned short)Duktape_GetIntPropertyValue(ctx, 0, "port", protocol == ILibWebClient_RequestToken_USE_HTTP ? 80 : 443), &dest);
if (dest.sin6_family == AF_UNSPEC)
{
duk_push_string(ctx, host);
duk_throw(ctx);
return(DUK_RET_ERROR);
}
if (isWebSocket == 0)
{
// Make PipelineStreamedRequest
token = ILibWebClient_PipelineStreamedRequest(wcm, (struct sockaddr*)&dest, packet, ILibDuktape_http_request_OnResponse, ILibDuktape_http_request_OnSendOK, ctx, NULL);
if (globalTunnel != NULL)
{
if (ILibHashtable_Get(globalTunnel->exceptionsTable, NULL, host, (int)hostLen) == NULL)
{
ILibWebClient_SetProxyEx(token, &(globalTunnel->proxyServer), (char*)globalTunnel->proxyUser, (char*)globalTunnel->proxyPass);
}
}
#ifndef MICROSTACK_NOTLS
ILibWebClient_Request_SetHTTPS(token, protocol);
ILibDuktape_http_requestClient_callbacks *cb = (ILibDuktape_http_requestClient_callbacks*)ILibDuktape_http_request_PUSH_clientRequest(ctx, token, 0);
cb->rejectUnauthorized = Duktape_GetIntPropertyValue(ctx, 0, "rejectUnauthorized", 0);
cb->checkServerIdentity = Duktape_GetHeapptrProperty(ctx, 0, "checkServerIdentity");
#else
ILibDuktape_http_request_PUSH_clientRequest(ctx, token, 0);
#endif
}
else
{
token = ILibWebClient_PipelineRequest(wcm, (struct sockaddr*)&dest, packet, ILibDuktape_http_request_WebSocket_OnResponse, ctx, NULL);
#ifndef MICROSTACK_NOTLS
ILibWebClient_Request_SetHTTPS(token, protocol);
#endif
if (globalTunnel != NULL)
{
if (ILibHashtable_Get(globalTunnel->exceptionsTable, NULL, host, (int)hostLen) == NULL)
{
ILibWebClient_SetProxyEx(token, &(globalTunnel->proxyServer), (char*)globalTunnel->proxyUser, (char*)globalTunnel->proxyPass);
}
}
#ifndef MICROSTACK_NOTLS
ILibDuktape_WebSocket_Pointers *ptrs = ILibDuktape_http_request_PUSH_clientRequest(ctx, token, 1);
ptrs->rejectUnauthorized = Duktape_GetIntPropertyValue(ctx, 0, "rejectUnauthorized", 0);
ptrs->checkServerIdentity = Duktape_GetHeapptrProperty(ctx, 0, "checkServerIdentity");
#else
ILibDuktape_http_request_PUSH_clientRequest(ctx, token, 1);
#endif
}
duk_dup(ctx, 0);
duk_put_prop_string(ctx, -2, HTTP_CLIENTREQUEST_PARAMETER);
duk_push_this(ctx);
duk_put_prop_string(ctx, -2, CLIENTREQUEST_HTTP);
if (nargs > 1) { ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter(ctx, -1), "response", duk_require_heapptr(ctx, 1)); }
return 1;
}
ILibTransport_DoneState ILibDuktape_http_request_write(ILibDuktape_WritableStream *stream, char *buffer, int bufferLen, void *user)
{
ILibWebClient_RequestToken token = (ILibWebClient_RequestToken)user;
ILibTransport_DoneState retVal = ILibTransport_DoneState_COMPLETE;
if (bufferLen > 0) { retVal = ILibWebClient_StreamRequestBody(token, buffer, bufferLen, ILibAsyncSocket_MemoryOwnership_USER, ILibTransport_DoneState_INCOMPLETE); }
return retVal;
}
void ILibDuktape_http_request_end(ILibDuktape_WritableStream *stream, void *user)
{
ILibWebClient_RequestToken token = (ILibWebClient_RequestToken)user;
ILibWebClient_StreamRequestBody(token, NULL, 0, ILibAsyncSocket_MemoryOwnership_USER, ILibTransport_DoneState_COMPLETE);
}
duk_ret_t ILibDuktape_http_clientRequest_upgrade_setter(duk_context *ctx)
{
duk_push_this(ctx); // [clientRequest]
duk_dup(ctx, 0); // [clientRequest][upgrade]
duk_put_prop_string(ctx, -2, "\xFF_Upgrade"); // [clientRequest]
return 0;
}
duk_ret_t ILibDuktape_http_request_finalizer(duk_context *ctx)
{
ILibWebClient_RequestToken token;
void **user;
if (duk_has_prop_string(ctx, 0, HTTP_CLIENTREQUEST_DATAPTR))
{
duk_get_prop_string(ctx, 0, HTTP_CLIENTREQUEST_DATAPTR);
duk_size_t bufLen;
char *buf = (char*)Duktape_GetBuffer(ctx, -1, &bufLen);
memset(buf, 0, bufLen);
}
if (duk_has_prop_string(ctx, 0, HTTP_REQUEST_TOKEN_PTR))
{
duk_get_prop_string(ctx, 0, HTTP_REQUEST_TOKEN_PTR);
token = duk_get_pointer(ctx, -1);
if (token != NULL)
{
user = (void**)ILibWebClient_RequestToken_GetUserObjects(token);
if (user != NULL)
{
user[0] = NULL;
user[1] = NULL;
}
ILibWebClient_CancelRequest(token);
}
}
return 0;
}
duk_ret_t ILibDuktape_http_request_no_op(duk_context *ctx)
{
return 0;
}
void* ILibDuktape_http_request_PUSH_clientRequest(duk_context *ctx, ILibWebClient_RequestToken token, int isWebSocket)
{
ILibDuktape_EventEmitter *emitter = NULL;
void **user = ILibWebClient_RequestToken_GetUserObjects(token);
if (user[1] != NULL && ((ILibDuktape_http_request_dataType*)user[1])->STRUCT_TYPE == ILibDuktape_http_request_dataType_request &&
((ILibDuktape_http_requestClient_callbacks*)user[1])->clientRequest != NULL)
{
duk_push_heapptr(ctx, ((ILibDuktape_http_requestClient_callbacks*)user[1])->clientRequest);
return(user[1]);
}
duk_push_object(ctx); // [obj]
duk_push_pointer(ctx, token); // [obj][token]
duk_put_prop_string(ctx, -2, HTTP_REQUEST_TOKEN_PTR); // [obj]
duk_push_fixed_buffer(ctx, isWebSocket == 0 ? sizeof(ILibDuktape_http_requestClient_callbacks) : sizeof(ILibDuktape_WebSocket_Pointers));
user[1] = Duktape_GetBuffer(ctx, -1, NULL);
((ILibDuktape_http_request_dataType*)user[1])->STRUCT_TYPE = isWebSocket == 0 ? ILibDuktape_http_request_dataType_request : ILibDuktape_http_request_dataType_webSocket;
duk_put_prop_string(ctx, -2, HTTP_CLIENTREQUEST_DATAPTR);
emitter = ILibDuktape_EventEmitter_Create(ctx);
ILibDuktape_EventEmitter_CreateEventEx(emitter, "upgrade");
ILibDuktape_EventEmitter_CreateEventEx(emitter, "error");
ILibDuktape_CreateFinalizer(ctx, ILibDuktape_http_request_finalizer);
if (((ILibDuktape_http_request_dataType*)user[1])->STRUCT_TYPE == ILibDuktape_http_request_dataType_request)
{
ILibDuktape_EventEmitter_CreateEvent(emitter, "response", &(((ILibDuktape_http_requestClient_callbacks*)user[1])->OnReceive));
((ILibDuktape_http_requestClient_callbacks*)user[1])->requestStream = ILibDuktape_WritableStream_Init(ctx, ILibDuktape_http_request_write, ILibDuktape_http_request_end, token);
((ILibDuktape_http_requestClient_callbacks*)user[1])->clientRequest = duk_get_heapptr(ctx, -1);
}
else
{
((ILibDuktape_WebSocket_Pointers*)user[1])->ctx = ctx;
((ILibDuktape_WebSocket_Pointers*)user[1])->clientRequest_ptr = duk_get_heapptr(ctx, -1);
ILibDuktape_CreateInstanceMethod(ctx, "end", ILibDuktape_http_request_no_op, 0);
}
return(user[1]);
}
duk_ret_t ILibDuktape_http_get(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
ILibWebClient_RequestToken token;
char *host;
unsigned short port;
char *path;
struct sockaddr_in6 dest;
duk_size_t uriLen;
char *uri;
int hostLen;
ILibDuktape_globalTunnel_data *globalTunnel = ILibDuktape_GetGlobalTunnel(ctx);
if (duk_is_string(ctx, 0))
{
uri = (char*)duk_get_lstring(ctx, 0, &uriLen);
}
else if (duk_is_object(ctx, 0))
{
uri = Duktape_GetStringPropertyValueEx(ctx, 0, "uri", "http://127.0.0.1/", &uriLen);
if (duk_has_prop_string(ctx, 0, "proxy"))
{
duk_get_prop_string(ctx, 0, "proxy");
globalTunnel = (ILibDuktape_globalTunnel_data*)ILibScratchPad;
memset(globalTunnel, 0, sizeof(ILibDuktape_globalTunnel_data));
ILibResolveEx(Duktape_GetStringPropertyValueEx(ctx, -1, "host", "127.0.0.1", NULL), (unsigned short)Duktape_GetIntPropertyValue(ctx, -1, "port", 8080), &(globalTunnel->proxyServer));
if (globalTunnel->proxyServer.sin6_family == AF_UNSPEC) { return(ILibDuktape_Error(ctx, "http.get(): Cannot resolve proxy host %s", Duktape_GetStringPropertyValueEx(ctx, -1, "host", "127.0.0.1", NULL))); }
}
else if (duk_has_prop_string(ctx, 0, "noProxy")) { globalTunnel = NULL; }
}
else
{
return(ILibDuktape_Error(ctx, "http.get(): Invalid parameter"));
}
ILibHTTPPacket *packet = ILibCreateEmptyPacket();
#ifndef MICROSTACK_NOTLS
ILibWebClient_RequestManager manager = ILibDuktape_http_GetRequestManager(ctx);
ILibParseUriResult result = ILibParseUri(uri, &host, &port, &path, &dest);
ILibWebClient_EnableHTTPS(manager, NULL, NULL, ILibDuktape_http_request_tls_verify);
#else
ILibParseUri(uri, &host, &port, &path, &dest);
#endif
ILibSetVersion(packet, "1.1", 3);
if (dest.sin6_family == AF_UNSPEC)
{
ILibDestructPacket(packet);
free(host); free(path);
duk_push_string(ctx, "Could not resolve URI");
duk_throw(ctx);
return(DUK_RET_ERROR);
}
hostLen = (int)strnlen_s(host, uriLen);
ILibSetDirective(packet, "GET", 3, path, (int)strnlen_s(path, uriLen));
ILibAddHeaderLine(packet, "Host", 4, host, hostLen);
if (duk_is_object(ctx, 0) && duk_has_prop_string(ctx, 0, "headers"))
{
duk_get_prop_string(ctx, 0, "headers");
duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY);
while (duk_next(ctx, -1, 1))
{
duk_size_t keyLen, valueLen;
char *key = (char*)duk_to_lstring(ctx, -2, &keyLen), *value = (char*)duk_to_lstring(ctx, -1, &valueLen);
ILibAddHeaderLine(packet, key, (int)keyLen, value, (int)valueLen);
duk_pop_2(ctx);
}
}
ILibAddHeaderLine(packet, "Content-Length", 14, "0", 1);
token = ILibWebClient_PipelineRequest(ILibDuktape_http_GetRequestManager(ctx), (struct sockaddr*)&dest, packet, ILibDuktape_http_request_OnResponse, ctx, NULL);
#ifndef MICROSTACK_NOTLS
ILibWebClient_Request_SetHTTPS(token, result == ILibParseUriResult_TLS ? ILibWebClient_RequestToken_USE_HTTPS : ILibWebClient_RequestToken_USE_HTTP);
#endif
if (globalTunnel != NULL)
{
if (ILibHashtable_Get(globalTunnel->exceptionsTable, NULL, host, hostLen) == NULL)
{
ILibWebClient_SetProxyEx(token, &(globalTunnel->proxyServer), (char*)globalTunnel->proxyUser, (char*)globalTunnel->proxyPass);
}
}
free(path); free(host);
ILibDuktape_http_request_PUSH_clientRequest(ctx, token, 0); // [clientRequest]
duk_dup(ctx, 0); // [clientRequest][param]
duk_put_prop_string(ctx, -2, HTTP_CLIENTREQUEST_PARAMETER); // [clientRequest]
duk_push_this(ctx); // [clientRequest][http]
duk_put_prop_string(ctx, -2, CLIENTREQUEST_HTTP); // [clientRequest]
if (nargs > 1)
{
ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter(ctx, -1), "response", duk_get_heapptr(ctx, 1));
}
return 1;
}
duk_ret_t ILibDuktape_http_finalizer(duk_context *ctx)
{
ILibWebClient_RequestManager wcm;
if (duk_has_prop_string(ctx, 0, HTTP_WEBCLIENT_MGR))
{
duk_get_prop_string(ctx, 0, HTTP_WEBCLIENT_MGR);
wcm = (ILibWebClient_RequestManager)duk_get_pointer(ctx, -1);
ILibChain_SafeRemove(((ILibChain_Link*)wcm)->ParentChain, wcm);
}
return 0;
}
duk_ret_t ILibDuktape_http_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;
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]
free(path);
free(addr);
}
else
{
duk_push_null(ctx);
}
return 1;
}
void ILibDuktape_http_stream_ServerResponse_sendHeaders(ILibDuktape_http_streamWrapper *wrapper, ILibHTTPPacket *headers)
{
char *data;
int dataLen = ILibGetRawPacket(headers, &data);
if (wrapper->PipedWriter != NULL)
{
((ILibDuktape_WritableStream*)wrapper->PipedWriter)->WriteSink(((ILibDuktape_WritableStream*)wrapper->PipedWriter), data, dataLen, ((ILibDuktape_WritableStream*)wrapper->PipedWriter)->WriteSink_User);
}
else
{
duk_push_heapptr(wrapper->ctx, wrapper->PipedReader); // [stream]
duk_get_prop_string(wrapper->ctx, -1, "write"); // [stream][func]
duk_swap_top(wrapper->ctx, -2); // [func][this]
duk_push_external_buffer(wrapper->ctx); // [func][this][chunk]
duk_config_buffer(wrapper->ctx, -1, data, dataLen);
if (duk_pcall_method(wrapper->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(wrapper->ctx, "http.httpStream.onWriteHead(): Error "); }
duk_pop(wrapper->ctx); // ...
}
free(data);
}
duk_ret_t ILibDuktape_http_stream_ServerResponse_writeHead(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
ILibHTTPPacket *headers;
ILibDuktape_http_streamWrapper *wrapper;
int statusCode = 200;
char *statusData = "OK";
duk_size_t statusDataLen = 2;
duk_push_this(ctx); // [response]
duk_get_prop_string(ctx, -1, HTTP_STREAM_WRAPPER); // [response][wrapper]
wrapper = (ILibDuktape_http_streamWrapper*)duk_get_pointer(ctx, -1);
headers = wrapper->impliedHeaders;
wrapper->impliedHeaders = NULL;
statusCode = duk_require_int(ctx, 0);
if (nargs > 1 && duk_is_string(ctx, 1))
{
statusData = (char*)duk_get_lstring(ctx, 1, &statusDataLen);
}
else
{
switch (statusCode)
{
case 100:
statusData = "Continue";
statusDataLen = 8;
break;
case 200:
statusData = "OK";
statusDataLen = 2;
break;
case 400:
statusData = "Bad Request";
statusDataLen = 11;
break;
case 401:
statusData = "Unauthorized";
statusDataLen = 12;
break;
case 404:
statusData = "Not Found";
statusDataLen = 9;
break;
case 500:
statusData = "Internal Server Error";
statusDataLen = 21;
break;
default:
statusData = "Unspecified";
statusDataLen = 11;
break;
}
}
ILibSetStatusCode(headers, statusCode, statusData, (int)statusDataLen);
ILibDuktape_http_stream_ServerResponse_sendHeaders(wrapper, headers);
ILibDestructPacket(headers);
return 0;
}
void ILibDuktape_http_stream_serverResponse_WriteSinkFlushedNative(struct ILibDuktape_WritableStream *stream, void *user)
{
ILibDuktape_http_streamWrapper *wrapper = (ILibDuktape_http_streamWrapper*)user;
ILibDuktape_WritableStream_Ready(wrapper->serverResponse_stream);
}
duk_ret_t ILibDuktape_http_stream_serverResponse_WriteSinkFlushed(duk_context *ctx)
{
duk_push_current_function(ctx); // [func]
duk_get_prop_string(ctx, -1, "\xFF_USER");
ILibDuktape_http_stream_serverResponse_WriteSinkFlushedNative(NULL, duk_get_pointer(ctx, -1));
return 0;
}
ILibTransport_DoneState ILibDuktape_http_stream_serverResponse_WriteSink(struct ILibDuktape_WritableStream *stream, char *buffer, int bufferLen, void *user)
{
ILibTransport_DoneState retVal = ILibTransport_DoneState_ERROR;
ILibDuktape_http_streamWrapper *wrapper = (ILibDuktape_http_streamWrapper*)user;
if (wrapper->impliedHeaders != NULL)
{
// Need to send out headers first
ILibDuktape_http_stream_ServerResponse_sendHeaders(wrapper, wrapper->impliedHeaders);
ILibDestructPacket(wrapper->impliedHeaders);
wrapper->impliedHeaders = NULL;
}
if (wrapper->PipedWriter != NULL)
{
ILibDuktape_WritableStream *ws = (ILibDuktape_WritableStream*)wrapper->PipedWriter;
ws->OnWriteFlushEx = ILibDuktape_http_stream_serverResponse_WriteSinkFlushedNative;
ws->OnWriteFlushEx_User = wrapper;
if (wrapper->chunkEncoded == 0)
{
return(ws->WriteSink(ws, buffer, bufferLen, ws->WriteSink_User));
}
else
{
int hexLen = sprintf_s(wrapper->hex, sizeof(wrapper->hex), "%X\r\n", bufferLen);
ws->WriteSink(ws, wrapper->hex, hexLen, ws->WriteSink_User);
ws->WriteSink(ws, buffer, bufferLen, ws->WriteSink_User);
return(ws->WriteSink(ws, "\r\n", 2, ws->WriteSink_User));
}
}
else
{
if (wrapper->chunkEncoded != 0)
{
int hexLen = sprintf_s(wrapper->hex, sizeof(wrapper->hex), "%X\r\n", bufferLen);
duk_push_heapptr(wrapper->ctx, wrapper->PipedReader); // [stream]
duk_get_prop_string(wrapper->ctx, -1, "write"); // [stream][func]
duk_swap_top(wrapper->ctx, -2); // [func][this]
duk_push_external_buffer(wrapper->ctx); // [func][this][chunk]
duk_config_buffer(wrapper->ctx, -1, wrapper->hex, hexLen);
if (duk_pcall_method(wrapper->ctx, 2) != 0)
{
ILibDuktape_Process_UncaughtExceptionEx(wrapper->ctx, "http.httpStream.onWrite(): Error ");
retVal = ILibTransport_DoneState_ERROR;
duk_pop(wrapper->ctx); // ...
return retVal;
}
duk_pop(wrapper->ctx); // ...
}
duk_push_heapptr(wrapper->ctx, wrapper->PipedReader); // [stream]
duk_get_prop_string(wrapper->ctx, -1, "write"); // [stream][func]
duk_swap_top(wrapper->ctx, -2); // [func][this]
duk_push_external_buffer(wrapper->ctx); // [func][this][chunk]
duk_config_buffer(wrapper->ctx, -1, buffer, bufferLen);
if (duk_pcall_method(wrapper->ctx, 1) != 0)
{
ILibDuktape_Process_UncaughtExceptionEx(wrapper->ctx, "http.httpStream.onWrite(): Error ");
retVal = ILibTransport_DoneState_ERROR;
duk_pop(wrapper->ctx);
return retVal;
}
else
{
retVal = duk_get_boolean(wrapper->ctx, -1) ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE;
}
duk_pop(wrapper->ctx); // ...
if (wrapper->chunkEncoded != 0)
{
duk_push_heapptr(wrapper->ctx, wrapper->PipedReader); // [stream]
duk_get_prop_string(wrapper->ctx, -1, "write"); // [stream][func]
duk_swap_top(wrapper->ctx, -2); // [func][this]
duk_push_external_buffer(wrapper->ctx); // [func][this][chunk]
duk_config_buffer(wrapper->ctx, -1, "\r\n", 2);
duk_push_c_function(wrapper->ctx, ILibDuktape_http_stream_serverResponse_WriteSinkFlushed, 0); // [func][this][chunk][callback]
duk_push_pointer(wrapper->ctx, wrapper); // [func][this][chunk][callback][ptr]
duk_put_prop_string(wrapper->ctx, -2, "\xFF_USER"); // [func][this][chunk][callback]
if (duk_pcall_method(wrapper->ctx, 2) != 0)
{
ILibDuktape_Process_UncaughtExceptionEx(wrapper->ctx, "http.httpStream.onWrite(): Error ");
retVal = ILibTransport_DoneState_ERROR;
}
else
{
retVal = duk_get_boolean(wrapper->ctx, -1) ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE;
}
duk_pop(wrapper->ctx);
}
}
return retVal;
}
void ILibDuktape_http_stream_serverResponse_EndSink(struct ILibDuktape_WritableStream *stream, void *user)
{
ILibDuktape_http_streamWrapper *wrapper = (ILibDuktape_http_streamWrapper*)user;
if (wrapper->chunkEncoded == 0)
{
// No choice but to propogate the End up, becuase we aren't chunked
if (wrapper->PipedWriter != NULL)
{
wrapper->PipedWriter->EndSink(wrapper->PipedWriter, wrapper->PipedWriter->WriteSink_User);
}
else
{
duk_push_heapptr(wrapper->ctx, wrapper->PipedReader); // [stream]
duk_get_prop_string(wrapper->ctx, -1, "end"); // [stream][func]
duk_swap_top(wrapper->ctx, -2); // [func][this]
if (duk_pcall_method(wrapper->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(wrapper->ctx, "http.httpStream.onEnd(): Error "); }
duk_pop(wrapper->ctx); // ...
}
}
else
{
// Since we're chunked, we can just write a zero length chunk
if (wrapper->PipedWriter != NULL)
{
wrapper->PipedWriter->WriteSink(wrapper->PipedWriter, "0\r\n\r\n", 5, wrapper->PipedWriter->WriteSink_User);
}
else
{
duk_push_heapptr(wrapper->ctx, wrapper->PipedReader); // [stream]
duk_get_prop_string(wrapper->ctx, -1, "write"); // [stream][func]
duk_swap_top(wrapper->ctx, -2); // [func][this]
duk_push_external_buffer(wrapper->ctx); // [func][this][chunk]
duk_config_buffer(wrapper->ctx, -1, "0\r\n\r\n", 5);
if (duk_pcall_method(wrapper->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(wrapper->ctx, "http.httpStream.onEnd(): Error "); }
duk_pop(wrapper->ctx); // ...
}
}
ILibWebClient_FinishedResponse_Server(wrapper->wcdo);
}
void ILibduktape_http_stream_PUSH_ServerResponse(duk_context *ctx, ILibDuktape_http_streamWrapper *wrapper)
{
duk_push_object(ctx); // [response]
wrapper->impliedHeaders = ILibCreateEmptyPacket();
if (wrapper->chunkEncoded == 0)
{
ILibSetVersion(wrapper->impliedHeaders, "1.0", 3);
}
else
{
ILibSetVersion(wrapper->impliedHeaders, "1.1", 3);
ILibAddHeaderLine(wrapper->impliedHeaders, "Transfer-Encoding", 17, "chunked", 7);
}
ILibSetStatusCode(wrapper->impliedHeaders, 200, "OK", 2);
duk_push_pointer(ctx, wrapper); // [response][wrapper]
duk_put_prop_string(ctx, -2, HTTP_STREAM_WRAPPER); // [response]
ILibDuktape_CreateInstanceMethod(ctx, "writeHead", ILibDuktape_http_stream_ServerResponse_writeHead, DUK_VARARGS);
wrapper->serverResponse_stream = ILibDuktape_WritableStream_Init(ctx, ILibDuktape_http_stream_serverResponse_WriteSink, ILibDuktape_http_stream_serverResponse_EndSink, wrapper);
}
void ILibDuktape_http_stream_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_http_streamWrapper *wrapper = (ILibDuktape_http_streamWrapper*)user1;
if (!(header->VersionLength == 3 && memcmp(header->Version, "1.0", 3) == 0)) { wrapper->chunkEncoded = 1; }
if (wrapper->OnRequest != NULL)
{
duk_push_heapptr(wrapper->ctx, wrapper->OnRequest); // [func]
duk_push_heapptr(wrapper->ctx, wrapper->self); // [func][this]
ILibDuktape_http_server_PUSH_IncomingMessage(wrapper->ctx, header, NULL); // [func][this][msg]
ILibduktape_http_stream_PUSH_ServerResponse(wrapper->ctx, wrapper); // [func][this][msg][rsp]
if (duk_pcall_method(wrapper->ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(wrapper->ctx, "http.httpStream.OnRequest(); Error "); }
duk_pop(wrapper->ctx); // ...
}
}
duk_ret_t ILibDuktape_http_stream_finalizer(duk_context *ctx)
{
duk_get_prop_string(ctx, 0, HTTP_STREAM_WRAPPER);
ILibDuktape_http_streamWrapper *wrapper = (ILibDuktape_http_streamWrapper*)Duktape_GetBuffer(ctx, -1, NULL);
if (wrapper->wcdo != NULL)
{
((int*)(wrapper->reserved + sizeof(ILibTransport)))[0] = ~0;
ILibWebClient_DestroyWebClientDataObject(wrapper->wcdo);
}
return 0;
}
ILibTransport_DoneState ILibDuktape_http_stream_WriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user)
{
ILibDuktape_http_streamWrapper *wrapper = (ILibDuktape_http_streamWrapper*)user;
ILibAsyncSocket_SocketModule module = (ILibAsyncSocket_SocketModule)wrapper->reserved;
int PAUSE = 0;
int i = 0, consumed = 0;
if (wrapper->bufferLen == 0)
{
do
{
consumed = 0;
ILibWebClient_OnData(module, buffer + i, &consumed, bufferLen, NULL, (void**)&(wrapper->wcdo), &PAUSE);
i += consumed;
bufferLen -= i;
} while (consumed != 0 && bufferLen != 0 && PAUSE == 0);
if (bufferLen != 0)
{
if (bufferLen > (int)(sizeof(wrapper->buffer) - wrapper->bufferLen)) { return(ILibTransport_DoneState_ERROR); }
if (wrapper->bufferLen + bufferLen > HTTP_STREAM_WRAPPER_BUFSIZE) { return(ILibTransport_DoneState_ERROR); }
memcpy_s(wrapper->buffer + wrapper->bufferLen, sizeof(wrapper->buffer) - wrapper->bufferLen, buffer + i, bufferLen);
wrapper->bufferLen += bufferLen;
}
}
else if (wrapper->bufferLen > 0)
{
if (wrapper->bufferLen + bufferLen > HTTP_STREAM_WRAPPER_BUFSIZE) { return(ILibTransport_DoneState_ERROR); }
memcpy_s(wrapper->buffer + wrapper->bufferLen, sizeof(wrapper->buffer) - wrapper->bufferLen, buffer, bufferLen);
wrapper->bufferLen += bufferLen;
i = 0;
do
{
consumed = 0;
ILibWebClient_OnData(module, wrapper->buffer + i, &consumed, wrapper->bufferLen, NULL, (void**)&(wrapper->wcdo), &PAUSE);
i += consumed;
wrapper->bufferLen -= i;
} while (consumed != 0 && wrapper->bufferLen != 0 && PAUSE == 0);
if (wrapper->bufferLen != 0)
{
memmove_s(wrapper->buffer, sizeof(wrapper->buffer), wrapper->buffer + i, wrapper->bufferLen);
}
}
return(PAUSE == 0 ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE);
}
void ILibDuktape_http_stream_EndSink(ILibDuktape_DuplexStream *ds, void *user)
{
ILibDuktape_http_streamWrapper *stream = (ILibDuktape_http_streamWrapper*)user;
if (stream->PipedWriter != NULL)
{
((ILibDuktape_WritableStream*)stream->PipedWriter)->EndSink((ILibDuktape_WritableStream*)stream->PipedWriter, ((ILibDuktape_WritableStream*)stream->PipedWriter)->WriteSink_User);
}
else
{
duk_push_heapptr(stream->ctx, stream->PipedReader); // [stream]
duk_get_prop_string(stream->ctx, -1, "end"); // [stream][end]
duk_swap_top(stream->ctx, -2); // [end][this]
if (duk_pcall_method(stream->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(stream->ctx, "http.httpStream.OnEnd(): Error "); }
duk_pop(stream->ctx); // ...
}
}
void ILibDuktape_http_stream_PauseSink(ILibDuktape_DuplexStream *sender, void *user)
{
ILibDuktape_http_streamWrapper *stream = (ILibDuktape_http_streamWrapper*)user;
if (stream->PipedWriter != NULL)
{
ILibDuktape_readableStream *rs = (ILibDuktape_readableStream*)stream->PipedReader;
if (rs->PauseHandler != NULL)
{
rs->PauseHandler(rs, rs->user);
}
else
{
duk_push_string(stream->ctx, "net.http.httpStream.OnPause(): Error, Native Readable Stream does not have a PauseHandler");
ILibDuktape_Process_UncaughtException(stream->ctx);
duk_pop(stream->ctx);
}
}
else
{
duk_push_heapptr(stream->ctx, stream->PipedReader); // [reader]
duk_get_prop_string(stream->ctx, -1, "pause"); // [reader][func]
duk_swap_top(stream->ctx, -2); // [func][this]
if (duk_pcall_method(stream->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(stream->ctx, "net.http.httpStream.OnPause(): Error "); }
duk_pop(stream->ctx); // ...
}
}
void ILibDuktape_http_stream_ResumeSink(ILibDuktape_DuplexStream *sender, void *user)
{
ILibDuktape_http_streamWrapper *stream = (ILibDuktape_http_streamWrapper*)user;
if (stream->PipedWriter != NULL)
{
ILibDuktape_readableStream *rs = (ILibDuktape_readableStream*)stream->PipedReader;
if (rs->ResumeHandler != NULL)
{
rs->ResumeHandler(rs, rs->user);
}
else
{
duk_push_string(stream->ctx, "net.http.httpStream.OnResume(): Error, Native Readable Stream does not have a ResumeHandler");
ILibDuktape_Process_UncaughtException(stream->ctx);
duk_pop(stream->ctx);
}
}
else
{
duk_push_heapptr(stream->ctx, stream->PipedReader); // [reader]
duk_get_prop_string(stream->ctx, -1, "resume"); // [reader][func]
duk_swap_top(stream->ctx, -2); // [func][this]
if (duk_pcall_method(stream->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(stream->ctx, "net.http.httpStream.OnResume(): Error "); }
duk_pop(stream->ctx); // ...
}
}
void ILibDuktape_http_Stream_PipeSink(duk_context *ctx, void *object, char *eventName, void *duk_eventArgs)
{
ILibDuktape_http_streamWrapper *stream;
duk_push_heapptr(ctx, object); // [stream]
duk_get_prop_string(ctx, -1, HTTP_STREAM_WRAPPER); // [stream][buffer]
stream = (ILibDuktape_http_streamWrapper*)Duktape_GetBuffer(ctx, -1, NULL);
duk_pop_2(ctx); // ...
duk_push_heapptr(ctx, duk_eventArgs); // [args]
if (duk_get_length(ctx, -1) > 0)
{
duk_get_prop_index(ctx, -1, 0); // [args][pipe]
if (duk_has_prop_string(ctx, -1, ILibDuktape_readableStream_RSPTRS) && duk_has_prop_string(ctx, -1, ILibDuktape_WritableStream_WSPTRS))
{
duk_get_prop_string(ctx, -1, ILibDuktape_readableStream_RSPTRS); // [args][pipe][rstream]
duk_get_prop_string(ctx, -2, ILibDuktape_WritableStream_WSPTRS); // [args][pipe][rstream][wstream]
stream->PipedReader = Duktape_GetBuffer(ctx, -2, NULL);
stream->PipedWriter = (ILibDuktape_WritableStream*)Duktape_GetBuffer(ctx, -1, NULL);
duk_pop_2(ctx); // [args][pipe]
}
else
{
stream->PipedReader = duk_get_heapptr(ctx, -1);
stream->PipedWriter = NULL;
}
duk_pop(ctx); // [args]
}
duk_pop(ctx); // ...
}
duk_ret_t ILibDuktape_http_createStream(duk_context *ctx)
{
ILibDuktape_http_streamWrapper *stream;
duk_push_object(ctx); // [stream]
duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_http_streamWrapper)); // [stream][buffer]
stream = (ILibDuktape_http_streamWrapper*)Duktape_GetBuffer(ctx, -1, NULL);
duk_put_prop_string(ctx, -2, HTTP_STREAM_WRAPPER); // [stream]
ILibDuktape_CreateFinalizer(ctx, ILibDuktape_http_stream_finalizer);
memset(stream, 0, sizeof(ILibDuktape_http_streamWrapper));
stream->ctx = ctx;
stream->self = duk_get_heapptr(ctx, -1);
stream->emitter = ILibDuktape_EventEmitter_Create(ctx);
ILibDuktape_EventEmitter_CreateEvent(stream->emitter, "request", &(stream->OnRequest));
ILibDuktape_EventEmitter_CreateEvent(stream->emitter, "response", &(stream->OnResponse));
stream->wcdo = ILibCreateWebClientEx(ILibDuktape_http_stream_OnReceive, (ILibAsyncSocket_SocketModule)(stream->reserved), stream, NULL);
stream->ds = ILibDuktape_DuplexStream_Init(ctx, ILibDuktape_http_stream_WriteSink, ILibDuktape_http_stream_EndSink, ILibDuktape_http_stream_PauseSink, ILibDuktape_http_stream_ResumeSink, stream);
ILibDuktape_EventEmitter_AddSink(stream->emitter, "pipe", ILibDuktape_http_Stream_PipeSink);
return 1;
}
void ILibDuktape_http_PUSH_EX(duk_context *ctx, void *chain, int https)
{
duk_push_object(ctx); // [http]
duk_push_pointer(ctx, chain); // [http][chain]
duk_put_prop_string(ctx, -2, "chain"); // [http]
duk_push_string(ctx, https == 0 ? "http:" : "https:");
duk_put_prop_string(ctx, -2, HTTP_DEFAULT_PROTO_KEY); // [http]
ILibDuktape_CreateInstanceMethod(ctx, "parseUri", ILibDuktape_http_parseUri, 1);
ILibDuktape_CreateInstanceMethod(ctx, "createServer", ILibDuktape_http_createServer, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "request", ILibDuktape_http_request, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "get", ILibDuktape_http_get, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "createStream", ILibDuktape_http_createStream, 0);
ILibDuktape_CreateFinalizer(ctx, ILibDuktape_http_finalizer);
duk_push_int(ctx, 5); // [http][pool]
duk_put_prop_string(ctx, -2, "RequestPoolSize"); // [http]
}
void ILibDuktape_http_PUSH(duk_context *ctx, void *chain)
{
ILibDuktape_http_PUSH_EX(ctx, chain, 0);
}
void ILibDuktape_https_PUSH(duk_context *ctx, void *chain)
{
ILibDuktape_http_PUSH_EX(ctx, chain, 1);
}
duk_ret_t ILibDuktape_httpDigest_clientRequest_onUpgrade(duk_context *ctx)
{
int i, nargs = duk_get_top(ctx);
duk_push_current_function(ctx); // [func]
duk_get_prop_string(ctx, -1, DIGEST_CLIENT_REQUEST); // [func][digestClientRequest]
duk_get_prop_string(ctx, -1, "emit"); // [func][digestClientRequest][emit]
duk_swap_top(ctx, -2); // [func][emit][this]
duk_push_string(ctx, "upgrade"); // [func][emit][this][upgrade]
for (i = 0; i < nargs; ++i)
{
duk_dup(ctx, i); // [func][emit][this][upgrade][params]
}
if (duk_pcall_method(ctx, 1 + nargs) != 0) { duk_throw(ctx); return(DUK_RET_ERROR); }
return(0);
}
duk_ret_t ILibDuktape_httpDigest_clientRequest_response2(duk_context *ctx)
{
ILibHTTPPacket *packet;
duk_get_prop_string(ctx, 0, "PacketPtr");
packet = (ILibHTTPPacket*)duk_get_pointer(ctx, -1);
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, "digestClientRequest");// [digestClientRequest]
if (packet->StatusCode == 200)
{
duk_get_prop_string(ctx, -1, "emit"); // [digestClientRequest][emit]
duk_swap_top(ctx, -2); // [emit][this]
duk_push_string(ctx, "response"); // [emit][this][response]
duk_dup(ctx, 0); // [emit][this][response][imsg]
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http-digest: Error dispatching response event"); }
}
return(0);
}
extern void* ILibWebClient_Digest_GenerateTable(ILibWebClient_StateObject state);
duk_ret_t ILibDuktape_httpDigest_clientRequest_response(duk_context *ctx)
{
ILibHTTPPacket *packet;
ILibWebClient_StateObject wcdo;
char *username, *password;
int tmpLen = 0;
char *uri = NULL;
void *digestClientPtr;
void *paramPtr = NULL;
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, "digestClientRequest");
digestClientPtr = duk_get_heapptr(ctx, -1);
duk_get_prop_string(ctx, -1, "digest");
duk_get_prop_string(ctx, -1, DIGEST_USERNAME);
username = (char*)duk_get_string(ctx, -1);
duk_get_prop_string(ctx, -2, DIGEST_PASSWORD);
password = (char*)duk_get_string(ctx, -1);
duk_get_prop_string(ctx, 0, "PacketPtr");
packet = (ILibHTTPPacket*)duk_get_pointer(ctx, -1);
if (packet->StatusCode == 401)
{
// UnAuthorized, need to retry request with Authorization Headers
duk_get_prop_string(ctx, 0, HTTP_INCOMINGMSG_WebStateObject);
wcdo = (ILibWebClient_StateObject)duk_get_pointer(ctx, -1);
int freePath = 0;
char *method, *path;
char result1[33];
char result2[33];
char result3[33];
void* table = ILibWebClient_Digest_GenerateTable(wcdo);
char* realm = (char*)ILibGetEntry(table, "realm", 5);
char* nonce = (char*)ILibGetEntry(table, "nonce", 5);
char* opaque = (char*)ILibGetEntry(table, "opaque", 6);
ILibDestroyHashTree(table);
duk_push_this(ctx); // [clientRequest]
duk_get_prop_string(ctx, -1, HTTP_CLIENTREQUEST_PARAMETER); // [clientRequest][param]
if (duk_is_string(ctx, -1))
{
// Parameter was a uri string
char *tmpHost;
unsigned short tmpPort;
uri = (char*)duk_get_string(ctx, -1);
ILibParseUri(uri, &tmpHost, &tmpPort, &path, NULL);
tmpLen = sprintf_s(ILibScratchPad2, sizeof(ILibScratchPad2), "%s:%s", (method = "GET"), path);
util_md5hex(ILibScratchPad2, tmpLen, result2);
free(tmpHost);
freePath = 1;
}
else
{
tmpLen = sprintf_s(ILibScratchPad2, sizeof(ILibScratchPad2), "%s:%s", (method = Duktape_GetStringPropertyValue(ctx, -1, "method", "GET")), (path = Duktape_GetStringPropertyValue(ctx, -1, "path", "/")));
util_md5hex(ILibScratchPad2, tmpLen, result2);
paramPtr = duk_get_heapptr(ctx, -1);
}
tmpLen = sprintf_s(ILibScratchPad2, sizeof(ILibScratchPad2), "%s:%s:%s", username, realm, password);
util_md5hex(ILibScratchPad2, tmpLen, result1);
tmpLen = sprintf_s(ILibScratchPad2, sizeof(ILibScratchPad2), "%s:%s:%s", result1, nonce, result2);
util_md5hex(ILibScratchPad2, tmpLen, result3);
tmpLen = sprintf_s(ILibScratchPad2, sizeof(ILibScratchPad2), "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", opaque=\"%s\", response=\"%s\"", username, realm, nonce, path, opaque, result3);
duk_push_this(ctx); // [clientReqeust]
duk_get_prop_string(ctx, -1, CLIENTREQUEST_HTTP); // [clientReqeust][http]
if (freePath != 0) { free(path); }
if (paramPtr == NULL)
{
duk_get_prop_string(ctx, -1, "get"); // [clientRequest][http][get]
duk_swap_top(ctx, -2); // [clientRequest][get][this]
duk_push_object(ctx); // [clientReqeust][get][this][options]
duk_push_string(ctx, uri);
duk_put_prop_string(ctx, -2, "uri");
}
else
{
duk_get_prop_string(ctx, -1, "request"); // [clientRequest][http][request]
duk_swap_top(ctx, -2); // [clientRequest][request][this]
duk_push_heapptr(ctx, paramPtr); // [clientRequest][request][this][options]
}
if(!duk_has_prop_string(ctx, -1, "headers"))
{
duk_push_object(ctx); // [clientReqeust][get][this][options][headers]
}
else
{
duk_get_prop_string(ctx, -1, "headers"); // [clientReqeust][get][this][options][headers]
}
duk_push_lstring(ctx, ILibScratchPad2, tmpLen); // [clientReqeust][get][this][options][headers][Auth]
duk_put_prop_string(ctx, -2, "Authorization"); // [clientReqeust][get][this][options][headers]
duk_put_prop_string(ctx, -2, "headers"); // [clientReqeust][get][this][options]
duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_response2, DUK_VARARGS); // [clientReqeust][get][this][options][callback]
duk_push_heapptr(ctx, digestClientPtr); // [clientReqeust][get][this][options][callback][digestClientRequest]
duk_put_prop_string(ctx, -2, "digestClientRequest"); // [clientReqeust][get][this][options][callback]
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "digest_onResponse: Error Invoking http.get"); }
duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_onUpgrade, 3); // [clientReqeust][OnUpgrade]
duk_push_heapptr(ctx, digestClientPtr); // [clientReqeust][OnUpgrade][digestClientRequest]
duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [clientReqeust][OnUpgrade]
ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter(ctx, -2), "upgrade", duk_get_heapptr(ctx, -1));
}
return(0);
}
duk_ret_t ILibDuktape_httpDigest_clientRequest_setter(duk_context *ctx)
{
duk_dup(ctx, 0); // [clientRequest]
duk_get_prop_string(ctx, -1, "once"); // [clientRequest][once]
duk_swap_top(ctx, -2); // [once][this]
duk_push_string(ctx, "response"); // [once][this][response]
duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_response, DUK_VARARGS); // [once][this][response][method]
duk_push_this(ctx); // [once][this][response][method][digest]
duk_put_prop_string(ctx, -2, "digest"); // [once][this][response][method]
if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http-digest: Error setting clientRequest"); }
duk_pop(ctx); // ..
return(0);
}
duk_ret_t ILibDuktape_httpDigest_http_setter(duk_context *ctx)
{
duk_push_this(ctx); // [digest]
duk_dup(ctx, 0); // [digest][http]
duk_put_prop_string(ctx, -2, HTTP_DIGEST); // [digest]
return(0);
}
duk_ret_t ILibDuktape_httpDigest_digestRequest_end(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
int i;
duk_push_this(ctx); // [digestClientRequest]
duk_get_prop_string(ctx, -1, DIGEST_CLIENT_REQUEST); // [digestClientRequest][clientRequest]
duk_get_prop_string(ctx, -1, "end"); // [digestClientRequest][clientRequest][end]
duk_swap_top(ctx, -2); // [digestClientRequest][end][this]
for (i = 0; i < nargs; ++i)
{
duk_dup(ctx, i); // [digestClientRequest][end][this][params...]
}
if (duk_pcall_method(ctx, nargs) != 0) { duk_throw(ctx); return(DUK_RET_ERROR); }
return(0);
}
duk_ret_t ILibDuktape_httpDigest_http_request(duk_context *ctx)
{
int nargs = duk_get_top(ctx);
void *clientRequest = NULL;
ILibDuktape_EventEmitter *emitter;
ILibDuktape_EventEmitter *crEmitter;
duk_push_current_function(ctx); // [func]
duk_get_prop_string(ctx, -1, "isGet"); // [func][isGet]
duk_push_this(ctx); // [func][isGet][digest]
duk_get_prop_string(ctx, -1, HTTP_DIGEST); // [func][isGet][digest][http]
if (duk_get_int(ctx, -3) != 0)
{
duk_get_prop_string(ctx, -1, "get"); // [func][isGet][digest][http][get]
}
else
{
duk_get_prop_string(ctx, -1, "request");// [func][isGet][digest][http][request]
}
duk_swap_top(ctx, -2); // [func][isGet][digest][get/request][this]
duk_dup(ctx, 0); // [func][isGet][digest][get/request][this][param1]
if (duk_pcall_method(ctx, 1) != 0) { duk_throw(ctx); return(DUK_RET_ERROR); }
// [clientRequest]
clientRequest = duk_get_heapptr(ctx, -1);
crEmitter = ILibDuktape_EventEmitter_GetEmitter(ctx, -1);
duk_get_prop_string(ctx, -1, "once"); // [clientRequest][once]
duk_swap_top(ctx, -2); // [once][this]
duk_push_string(ctx, "response"); // [once][this][response]
duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_response, DUK_VARARGS); // [once][this][response][method]
duk_push_object(ctx); // [once][this][response][method][digest-clientRequest]
duk_push_heapptr(ctx, clientRequest); // [once][this][response][method][digest-clientRequest][clientRequest]
duk_dup(ctx, -2); // [once][this][response][method][digest-clientRequest][clientRequest][digest-clientRequest]
duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [once][this][response][method][digest-clientRequest][clientRequest]
duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [once][this][response][method][digest-clientRequest]
emitter = ILibDuktape_EventEmitter_Create(ctx);
ILibDuktape_EventEmitter_CreateEventEx(emitter, "response");
ILibDuktape_EventEmitter_CreateEventEx(emitter, "error");
ILibDuktape_EventEmitter_CreateEventEx(emitter, "upgrade");
ILibDuktape_CreateInstanceMethod(ctx, "end", ILibDuktape_httpDigest_digestRequest_end, DUK_VARARGS);
if (nargs > 1 && duk_is_function(ctx, 1))
{
ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter(ctx, -1), "response", duk_require_heapptr(ctx, 1));
}
duk_push_this(ctx); // [once][this][response][method][digest-clientRequest][digest]
duk_put_prop_string(ctx, -2, "digest"); // [once][this][response][method][digest-clientRequest]
duk_put_prop_string(ctx, -2, "digestClientRequest"); // [once][this][response][method]
if (duk_pcall_method(ctx, 2) != 0) { duk_throw(ctx); return(DUK_RET_ERROR); }
// [clientRequest]
duk_push_heapptr(emitter->ctx, emitter->object); // [digestClientRequest]
duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_onUpgrade, 3); // [digestClientRequest][OnUpgrade]
duk_dup(ctx, -2); // [digestClientRequest][OnUpgrade][digestClientRequest]
duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [digestClientRequest][OnUpgrade]
ILibDuktape_EventEmitter_AddOnce(crEmitter, "upgrade", duk_get_heapptr(ctx, -1));
duk_pop(ctx); // [digestClientRequest]
return(1);
}
duk_ret_t ILibduktape_httpDigest_create(duk_context *ctx)
{
duk_size_t usernameLen, passwordLen;
char *username = (char*)duk_require_lstring(ctx, 0, &usernameLen), *password = (char*)duk_require_lstring(ctx, 1, &passwordLen);
ILibDuktape_EventEmitter *emitter;
duk_push_object(ctx); // [obj]
ILibDuktape_CreateEventWithSetterEx(ctx, "clientRequest", ILibDuktape_httpDigest_clientRequest_setter);
ILibDuktape_CreateEventWithSetterEx(ctx, "http", ILibDuktape_httpDigest_http_setter);
emitter = ILibDuktape_EventEmitter_Create(ctx);
ILibDuktape_EventEmitter_CreateEventEx(emitter, "response");
ILibDuktape_EventEmitter_CreateEventEx(emitter, "error");
ILibDuktape_EventEmitter_CreateEventEx(emitter, "upgrade");
ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "isGet", 1, "get", ILibDuktape_httpDigest_http_request, DUK_VARARGS);
ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "isGet", 0, "request", ILibDuktape_httpDigest_http_request, DUK_VARARGS);
duk_push_string(ctx, username);
duk_put_prop_string(ctx, -2, DIGEST_USERNAME);
duk_push_string(ctx, password);
duk_put_prop_string(ctx, -2, DIGEST_PASSWORD);
return(1);
}
void ILibDuktape_httpDigest_PUSH(duk_context *ctx, void *chain)
{
duk_push_object(ctx);
ILibDuktape_CreateInstanceMethod(ctx, "create", ILibduktape_httpDigest_create, 2);
}
duk_ret_t ILibDuktape_httpHeaders(duk_context *ctx)
{
ILibHTTPPacket *packet = NULL;
packetheader_field_node *node;
int headersOnly = duk_get_top(ctx) > 1 ? (duk_require_boolean(ctx, 1) ? 1 : 0) : 0;
duk_size_t bufferLen;
char *buffer = (char*)Duktape_GetBuffer(ctx, 0, &bufferLen);
packet = ILibParsePacketHeader(buffer, 0, (int)bufferLen);
if (packet == NULL) { return(ILibDuktape_Error(ctx, "http-headers(): Error parsing data")); }
if (headersOnly == 0)
{
duk_push_object(ctx);
if (packet->Directive != NULL)
{
duk_push_lstring(ctx, packet->Directive, packet->DirectiveLength);
duk_put_prop_string(ctx, -2, "method");
duk_push_lstring(ctx, packet->DirectiveObj, packet->DirectiveObjLength);
duk_put_prop_string(ctx, -2, "url");
}
else
{
duk_push_int(ctx, packet->StatusCode);
duk_put_prop_string(ctx, -2, "statusCode");
duk_push_lstring(ctx, packet->StatusData, packet->StatusDataLength);
duk_put_prop_string(ctx, -2, "statusMessage");
}
if (packet->VersionLength == 3)
{
duk_push_object(ctx);
duk_push_lstring(ctx, packet->Version, 1);
duk_put_prop_string(ctx, -2, "major");
duk_push_lstring(ctx, packet->Version + 2, 1);
duk_put_prop_string(ctx, -2, "minor");
duk_put_prop_string(ctx, -2, "version");
}
}
duk_push_object(ctx); // headers
node = packet->FirstField;
while (node != NULL)
{
duk_push_lstring(ctx, node->Field, node->FieldLength);
duk_push_lstring(ctx, node->FieldData, node->FieldDataLength);
duk_put_prop(ctx, -3);
node = node->NextField;
}
if (headersOnly == 0)
{
duk_put_prop_string(ctx, -2, "headers");
}
ILibDestructPacket(packet);
return(1);
}
void ILibDuktape_httpHeaders_PUSH(duk_context *ctx, void *chain)
{
duk_push_c_function(ctx, ILibDuktape_httpHeaders, DUK_VARARGS);
}
void ILibDuktape_http_init(duk_context * ctx, void * chain)
{
ILibDuktape_ModSearch_AddHandler(ctx, "http", ILibDuktape_http_PUSH);
ILibDuktape_ModSearch_AddHandler(ctx, "https", ILibDuktape_https_PUSH);
ILibDuktape_ModSearch_AddHandler(ctx, "http-digest", ILibDuktape_httpDigest_PUSH);
ILibDuktape_ModSearch_AddHandler(ctx, "http-headers", ILibDuktape_httpHeaders_PUSH);
}
#ifdef __DOXY__
/*!
\brief Http exposed using Node APIs. <b>Note:</b> To use, must <b>require('http')</b> or <b>require('https')</b>
*/
class Http
{
public:
/*!
\brief Parses a uri string
\param str \<String\> The uri to parse
\return Uri object of the parsed string
*/
static Uri parseUri(str);
/*!
\brief Returns a new instance of Server
*
static Server createServer([options][, callback]);
\param options <Object> that specifies various parameters such as:\n
'request' = <func> callback to be dispatched when a request is received\n
'pfx' = \<Buffer\|String\> containing PKS encoded certificate\n
'passphrase' = \<String\> containing the passphrase to unlock the private key of the specified certificate\n
'MeshAgent' = <MeshAgent> object, whose certificates we are going to use.\n
'requestCert' = <boolean> indicating if the client certificate will be requested\n
'rejectUnauthorized' = <boolean> indicating if the certificate must have a valid root of trust\n
'checkClientIdentity' = <func> callback to be dispatched to verify client certificate. <b>Note:</b> When dispatched, throw an exception to fail verification.\n
\param callback <func> callback to be dispatched when a request is received
\return Server instance.
*/
static Server createServer([options][, callback]);
/*!
\brief Issues an HTTP Request onto the network
*
static ClientRequest request(options[, callback]);
\param options \<Object\|String\|Url\>\n
<b>protocol</b> \<String\> Protocol to use. Defaults to 'http:' or 'https:' depending on configuration\n
<b>host</b> \<String\> A domain name or IP address of the server to issue the request to. Defaults to localhost.\n
<b>hostname</b> \<String\> Alias for host. To support url.parse(), hostname is preferred over host.\n
<b>port</b> <number> Port of remote server. Defaults to 80, or 443, depending on configuration\n
<b>localAddress</b> \<String\> Local interface to bind for network connections.\n
<b>method</b> \<String\> A string specifying the HTTP request method. Defaults to 'GET'.\n
<b>path</b> \<String\> Request path. Defaults to '/'\n
<b>headers</b> <Object> An object containing request headers.\n
<b>proxy</b> <Object> An object containing proxy settings, (ie: 'host' and 'port'), to use for this connection\n
<b>noProxy</b> <boolean> When present, will override any previously set proxies, and disable it for this connection\n
<b>pfx</b> \<Buffer\|String\> containing pks encoded certificate\n
<b>passphrase</b> \<String\> containing passphrase used to unlock specified certificate\n
<b>MeshAgent</b> <MeshAgent> containing the MeshAgent instance, whose certificates will be used\n
<b>rejectUnauthorized</b> <boolean> If true, will reject server's whose root of trust cannot be validated\n
<b>checkServerIdentity</b> <func> callback that will be dispatched to validate server certificate. <b>Note:</b> To fail validation, throw an exception from the dispatch.\n
\param callback <func> Optional. Set as one time listener for ClientRequest.response event.
\return ClientRequest instance.
*/
static ClientRequest request(options[, callback]);
/*!
\brief Issues an HTTP Request onto the network.
\param url \<String\> The url to issue a GET reqeust
\return ClientRequest instance
*/
static ClientRequest get(url);
static DuplexStream createStream();
/*!
\implements EventEmitter
\brief Http Server Abstraction
*/
class Server
{
public:
/*!
\brief Event emitted each time a request with an HTTP Expect: 100-continue is received. If this event is not listened for, the server will automatically respond with a 100 Continue as appropriate.
*
<b>Note:</b> When this event is emitted and handled, Http.request event will not be emitted.
\param request \<IncomingMessage\>
\param response \<ServerResponse\>
*/
void checkContinue;
/*!
\brief Event emitted each time a request with an HTTP Expect header is received, where the value is not 100-continue. If this event is not listened for, the server will automatically respond with a 417 Expectation Failed as appropriate.
*
<b>Note:</b> When this event is emitted and handled, Http.request event will not be emitted.
*/
void checkExpectation;
/*!
\brief Event emitted if the underlying Socket emits an error
\param err <Error>
\param socket \<Socket\>
*/
void clientError;
/*!
\brief Event emitted when the Server closes.
*/
void close;
/*!
\brief Event emitted when the client issues a 'CONNECT' method. If this event is not listened for, then clients requesting a CONNECT method will have their connections closed.
\param request \<IncomingMessage\> Arguments for the HTTP request, as it is in the 'request' event
\param socket \<Socket\> Network socket between the server and client
\param head \<Buffer\> The first packet of the tunneling stream (may be empty)
*/
void connect;
/*!
\brief Event emitted each time a client requests an HTTP upgrade. If this event is not listened for, then clients requesting an upgrade will have their connections closed.
\param request \<IncomingMessage\> Arguments for the HTTP request, as it is in the 'request' event
\param socket \<Socket\> Network socket between the server and client
\param head \<Buffer\> The first packet of the tunneling stream (may be empty)
*/
void upgrade;
/*!
\brief Event emitted each time there is a request. <b>Note:</b> There may be multiple requests per connection (in the case of HTTP Keep-Alive connections).
\param request \<IncomingMessage\>
\param response \<ServerResponse\>
*/
void request;
/*!
\brief Event emitted when server is listening for incoming connections
*/
void listening;
/*!
\brief Stops the server from accepting new connections
\param callback <func> Optional. Set as one time listener for 'close' event.
*/
void close([callback]);
/*!
\brief Event emitted when an idle timeout has elapsed
\param socket \<Socket\> Timed out socket
*/
void timeout;
/*!
\brief Begin accepting connections on the specified port and hostname
\param port <Integer>
\param hostname \<String\>
\param backlog <Integer>
\param callback <func> Optional. Set as one time listener to 'listening' event
*/
void listen([port][, hostname][, backlog][, callback]);
/*!
\brief Sets the timeout value for sockets, and emits a 'timeout' event on the Server object, passing the socket as an argument, if a timeout occurs.
\param msecs <Integer> Optional <b>Default: 120000</b> milliseconds (2 minutes)
\param callback <func> Optional. Set as one time listener for 'timeout' event
*/
void setTimeout([msecs][, callback]);
/*!
\implements WritableStream
\implements EventEmitter
\brief Created internally by Http.
*/
class ServerResponse
{
public:
/*!
\brief Sets a single header value for implicit headers.If this header already exists in the to - be - sent headers, its value will be replaced
\param name \<String\>
\param value \<String\>
*/
void setHeader(name, value);
/*!
\brief Sends a response header to the request.
\param statusCode <Integer> 3 digit code (ie: 200, 404, etc)
\param statusMessage \<String\> Human readable status message (ie: 'OK', 'File Not Found', etc)
\param headers <Object> Optional. JSON object where each name/value pair is a header/value pair.
*/
void writeHead(statusCode[, statusMessage][, headers]);
/*!
\brief When using implicit headers (not calling writeHead() explicitly), this property controls the status code that will be sent to the client when the headers get flushed.
*/
Integer statusCode;
/*!
\brief When using implicit headers (not calling writeHead() explicitly), this property controls the status message that will be sent to the client when the headers get flushed
*/
String statusMessage;
};
};
/*!
\implements WritableStream
\brief This object is created internally and returned from http.request(). It represents an in-progress request whose header has already been queued.
*/
class ClientRequest
{
public:
/*!
\brief Event emitted when a response is received to this request. This event is emitted only once.
\param msg IncomingMessage object containing the received request
*/
void response;
/*!
\brief Event emitted each time a server responds to a request with an upgrade.
\param response IncomingMessage
\param socket WebSocket
\param head <Buffer>
*/
void upgrade;
};
/*!
\implements Socket
\brief WebSocket abstraction
*/
class WebSocket
{
public:
/*!
\brief Event emitted when a 'ping' web socket control packet is received.
\param data \<Buffer\|String\|NULL\> Optional data that was attached to the received 'ping' control packet.
*/
void ping;
/*!
\brief Event emitted when a 'pong' web socket control packet is received.
\param data \<Buffer\|String\|NULL\> Optional data that was attached to the received 'pong' control packet.
*/
void pong;
/*!
\brief Send a 'ping' web socket control packet to the connected peer.
*
void ping([data]);
\param data \<Buffer\|String\|NULL\> Optional data to attach to the 'ping' control packet.
*/
void ping([data]);
/*!
\brief Send a 'pong' web socket control packet to the connected peer.
*
void pong([data]);
\param data \<Buffer\|String\|NULL\> Optional data to attach to the 'pong' control packet.
*/
void pong([data]);
};
/*!
\brief An IncomingMessage object may be used to access response status, headers and data.
*/
class IncomingMessage
{
public:
/*!
\brief Key-value pairs of header names and values. Header names are lower-cased
*/
Object headers;
/*!
\brief HTTP Version sent by client. Usually either '1.0' or '1.1'
*/
String httpVersion;
/*!
\brief Request Method as a String. (ie: GET, PUT, etc)
*/
String method;
/*!
\brief The Socket object associated with this connection
*/
Socket socket;
/*!
\brief 3 digit HTTP Status Code. (ie: 200, 404, etc)
*/
integer statusCode;
/*!
\brief HTTP Status Message (ie: 'OK', 'File Not Found', etc)
*/
String statusMessage;
/*!
\brief HTTP Request Path line (ie: '/index.html', etc)
*/
String url;
};
/*!
\brief Network Uri abstraction
*/
class Uri
{
public:
/*!
\brief Protocol (ie: http, https, wss, etc)
*/
String protocol;
/*!
\brief Host IP or DNS Name
*/
String host;
/*!
\brief Host port
*/
integer port;
/*!
\brief Method Path (ie: /index.html)
*/
String path;
/*!
\brief Method. (ie: GET, PUT, HEAD, etc)
*/
String method;
};
};
/*!
\brief Provides HTTP-Digest Authentication Services. <b>Note:</b> To use must <b>require('http-digest').Create()</b>
*
After creation, the 'http' property must be set, typically with <b>require('http')</b>. Afterwards, calls to 'request' can be made.
*/
class HttpDigest
{
public:
/*!
\brief Initializes an HttpDigest object with the specified username and password.
\param username \<String\> The username to encode
\param password \<String\> The password to encode
\return HttpDigest instance
*/
static HttpDigest Create(username, password);
/*!
\brief Wrapped Http implementation, which <b>must</b> be set. Typically set to <b>require('http')</b> or <b>require('https')</b>
*/
Http http;
/*!
\brief Issues a Digest-Authenticated HTTP Request onto the network
*
static DigestClientRequest request(options[, callback]);
\param options \<Object\|String\|Url\>\n
<b>protocol</b> \<String\> Protocol to use. Defaults to 'http:' or 'https:' depending on configuration\n
<b>host</b> \<String\> A domain name or IP address of the server to issue the request to. Defaults to localhost.\n
<b>hostname</b> \<String\> Alias for host. To support url.parse(), hostname is preferred over host.\n
<b>port</b> <number> Port of remote server. Defaults to 80, or 443, depending on configuration\n
<b>localAddress</b> \<String\> Local interface to bind for network connections.\n
<b>method</b> \<String\> A string specifying the HTTP request method. Defaults to 'GET'.\n
<b>path</b> \<String\> Request path. Defaults to '/'\n
<b>headers</b> <Object> An object containing request headers.\n
<b>proxy</b> <Object> An object containing proxy settings, (ie: 'host' and 'port'), to use for this connection\n
<b>noProxy</b> <boolean> When present, will override any previously set proxies, and disable it for this connection\n
<b>pfx</b> \<Buffer\|String\> containing pks encoded certificate\n
<b>passphrase</b> \<String\> containing passphrase used to unlock specified certificate\n
<b>MeshAgent</b> <MeshAgent> containing the MeshAgent instance, whose certificates will be used\n
<b>rejectUnauthorized</b> <boolean> If true, will reject server's whose root of trust cannot be validated\n
<b>checkServerIdentity</b> <func> callback that will be dispatched to validate server certificate. <b>Note:</b> To fail validation, throw an exception from the dispatch.\n
\param callback <func> Optiona. Set as one time listener to DigestClientRequest.response event.
\return \<DigestClientRequest\>
*/
DigestClientRequest request(options[, callback]);
/*!
\implements Http::ClientRequest
\brief Encapsulation of Http::ClientRequest. Digest-Authentication may require multiple request/response sequences, so the underlying Http::ClientRequest may change
*/
class DigestClientRequest
{
};
};
/*!
\brief Helper function to parse HTTP Headers. <b>Note:</b> To use, must <b>require('http-headers')</b>
*/
class HttpHeaders
{
public:
/*!
\brief Parses the specified buffer
*
static HttpHeaders HttpHeaders(data[, headersOnly]);
\param data \<Buffer\|String\> The data to parse
\param headersOnly <boolean> Optional parameter, that if true, will indicate to the parser to skip parsing of the Method/Path/Version/etc.
\return HttpHeaders representing the parsed data
*/
static HttpHeaders HttpHeaders(data[, headersOnly]);
/*!
\brief HTTP Method. (ie: GET, PUT, HEAD, etc)
*/
public String method;
/*!
\brief HTTP Method Path (ie: /index.html)
*/
public String url;
/*!
\brief HTTP Status Code (ie: 200)
*/
public integer statusCode;
/*!
\brief HTTP Status Code Message (ie: OK)
*/
public String statusMessage;
/*!
\brief HttpVersion of the decoded HTTP headers
*/
public HttpVersion version;
/*!
\brief JSON object of decoded HTTP headers. Property key is header name, Property value is header value.
*/
public object headers;
/*!
\brief HTTP Version
*/
class HttpVersion
{
public:
/*!
\brief major version
*/
public String major;
/*!
\brief minor version
*/
public String minor;
};
};
#endif