"linux": ["m", "rt"],
"darwin": [],
"nuttx": [],
- "tizen": ["m", "rt"],
+ "tizen": ["m", "rt", "curl"],
"tizenrt": []
}
},
"module": {
"always": ["buffer", "console", "events", "fs", "module", "timers"],
- "include": ["assert", "dns", "http", "net", "stream", "testdriver"],
+ "include": ["assert", "dns", "http", "net", "stream", "testdriver", "https"],
"exclude": {
"all": [],
- "linux": ["adc", "ble", "dgram", "gpio", "i2c", "pwm", "spi", "uart"],
- "nuttx": ["adc", "dgram", "gpio", "i2c", "pwm", "stm32f4dis", "uart"],
- "darwin": [],
- "tizenrt": ["gpio", "pwm"]
+ "linux": ["adc", "ble", "dgram", "gpio", "i2c", "pwm", "spi", "uart", "https"],
+ "nuttx": ["adc", "dgram", "gpio", "i2c", "pwm", "stm32f4dis", "uart", "https"],
+ "darwin": ["https"],
+ "tizen": ["adc", "ble", "dgram", "gpio", "i2c", "pwm", "spi", "uart"],
+ "tizenrt": ["gpio", "pwm", "https"]
}
}
}
--- /dev/null
+### Platform Support
+
+ The following shows Https module APIs available for each platform.
+
+| | Linux<br/>(Ubuntu) | Raspbian<br/>(Raspberry Pi) | Nuttx<br/>(STM32F4-Discovery) | Tizen<br/>(Artik 10) |
+| :---: | :---: | :---: | :---: | :---: |
+| https.request | X | X | X | O |
+| https.get | X | X | X | O |
+
+
+# Https
+
+IoT.js provides HTTPS to support HTTPS clients enabling users to send HTTPS request easily.
+
+### https.request(options[, callback])
+* `options` {Object}
+ * `host` {string} A domain name or IP address of the server to issue the request to. Defaults to 'localhost'.
+ * `hostname` {string} Alias for host.
+ * `port` {number} Port of remote server. Defaults to 80.
+ * `method` {string} A string specifying the HTTPS request method. Defaults to 'GET'.
+ * `path` {string} Request path. Defaults to '/'. Should include query string if any. E.G. '/index.html?page=12'. An exception is thrown when the request path contains illegal characters. Currently, only spaces are rejected but that may change in the future.
+ * `headers` {Object} An object containing request headers.
+ * `auth` {string} Optional Basic Authentication in the form `username:password`. Used to compute HTTPS Basic Authentication header.
+ * `ca` {string} Optional file path to CA certificate. Allows to override system trusted CA certificates.
+ * `cert` {string} Optional file path to client authentication certificate in PEM format.
+ * `key1` {string} Optional file path to private keys for client cert in PEM format.
+* `callback` {Function}
+ * `response` {https.IncomingMessage}
+* Returns: {https.ClientRequest}
+
+Example:
+```javascript
+var https = require('https');
+
+var request = https.request({
+ method: 'POST',
+ port: 443,
+ headers: {'Content-Length': 3}
+});
+
+...
+
+request.end();
+```
+
+Note that in the example `req.end()` was called. With `https.request()` one must always call `req.end()` to signify that you're done with the request - even if there is no data being written to the request body.
+
+### https.get(options[, callback])
+* `options` {Object}
+* `callback` {Function}
+ * `response` {https.IncomingMessage}
+* Returns: {https.ClientRequest}
+
+Same as `https.request` except that `https.get` automatically call `req.end()` at the end.
+
+Example:
+```javascript
+var https = require('https');
+
+https.get({
+ port: 80,
+}, function(res) {
+...
+});
+```
+
+
+### https.METHODS
+A list of HTTPS methods supported by the parser as `string` properties of an `Object`. Currently supported methods are `'DELETE'`, `'GET'`, `'HEAD'`, `'POST'`, `'PUT'`, `'CONNECT'`, `'OPTIONS'`, `'TRACE'`.
+
+
+## Class: https.ClientRequest
+
+This object is created internally and returned from https.request(). It represents an in-progress request whose header has already been queued.
+
+https.ClientRequest inherits [`Stream.writable`](IoT.js-API-Stream.md). See it's documentation to write data to an outgoing HTTP request. Notable methods are `'writable.write()'` (to send data as request body), `'writable.end()'` (to signal an end and flush remaining request body), and the event `'finish'`.
+
+### request.aborted
+If the request has been aborted, this contains the time at which the request was aborted in milliseconds since epoch as `Number`.
+
+### Event: 'close'
+This event is fired when the underlying socket is closed.
+
+### Event: 'response'
+* `response` {https.IncomingMessage}
+
+This event is emitted when server's response header is parsed. ` https.IncomingMessage` object is passed as argument to handler.
+
+### Event: 'socket'
+This event is emitted when a socket is assigned to this request.
+
+### request.abort()
+Will abort the outgoing request, dropping any data to be sent/received and destroying the underlying socket.
+
+### request.setTimeout(ms, cb)
+* `ms` {number}
+* `cb` {Function}
+
+Registers cb for 'timeout' event and set socket's timeout value to ms. This event will be triggered by the underlying socket's 'timeout' event.
+
+If cb is not provided, the socket will be destroyed automatically after timeout.
+If you provides cb, you should handle the socket's timeout.
+
+
+## Class: https.IncomingMessage
+
+This object is created internally and returned to the callback in https.request(). It represents the response sent by a server to a request.
+
+https.IncomingMessage inherits [`Stream.readable`](IoT.js-API-Stream.md). See it's documentation to read incoming data from an HTTP request. Notable events are `'data'` (fired when there is data to read), `'close'`, `'end'` (Request has ended) and the method `readable.read()`.
+
+### message.headers
+HTTPS header object.
+
+### message.method
+Request's method as `string`
+
+### message.statusCode
+HTTPS response status code as `number` of 3-digit.
+
+### message.statusMessage
+HTTPS response status message as `string`
+
+### message.url
+Requests URL as `string`
+
+### message.setTimeout(ms, cb)
+* `ms` {number}
+* `cb` {Function}
+
+Registers cb for 'timeout' event set socket's timeout value to ms. This event will be triggered by the underlying socket's 'timeout' event.
+
+If cb is not provided, the socket will be destroyed automatically after timeout.
+If you provides cb, you should handle the socket's timeout.
#define IOTJS_MAGIC_STRING_1 "1"
#define IOTJS_MAGIC_STRING_2 "2"
#define IOTJS_MAGIC_STRING_3 "3"
+#define IOTJS_MAGIC_STRING_ABORT "abort"
#define IOTJS_MAGIC_STRING_ADC "Adc"
+#define IOTJS_MAGIC_STRING_ADDHEADER "addHeader"
#define IOTJS_MAGIC_STRING_ADDMEMBERSHIP "addMembership"
#define IOTJS_MAGIC_STRING_ADDRESS "address"
#define IOTJS_MAGIC_STRING_ARCH "arch"
#define IOTJS_MAGIC_STRING__BUILTIN "_builtin"
#define IOTJS_MAGIC_STRING_BYTELENGTH "byteLength"
#define IOTJS_MAGIC_STRING_BYTEPARSED "byteParsed"
+#define IOTJS_MAGIC_STRING_CA "ca"
+#define IOTJS_MAGIC_STRING_CERT "cert"
#define IOTJS_MAGIC_STRING_CHDIR "chdir"
#define IOTJS_MAGIC_STRING_CHIP "chip"
#define IOTJS_MAGIC_STRING_CHIPSELECT "chipSelect"
#define IOTJS_MAGIC_STRING_COMPILENATIVEPTR "compileNativePtr"
#define IOTJS_MAGIC_STRING_CONNECT "connect"
#define IOTJS_MAGIC_STRING_COPY "copy"
+#define IOTJS_MAGIC_STRING_CREATEREQUEST "createRequest"
#define IOTJS_MAGIC_STRING__CREATESTAT "_createStat"
#define IOTJS_MAGIC_STRING_CREATETCP "createTCP"
#define IOTJS_MAGIC_STRING_CWD "cwd"
#define IOTJS_MAGIC_STRING_FALLING_U "FALLING"
#define IOTJS_MAGIC_STRING_FAMILY "family"
#define IOTJS_MAGIC_STRING_FINISH "finish"
+#define IOTJS_MAGIC_STRING_FINISHREQUEST "finishRequest"
#define IOTJS_MAGIC_STRING_FLOAT "FLOAT"
#define IOTJS_MAGIC_STRING_FSTAT "fstat"
#define IOTJS_MAGIC_STRING_GETADDRINFO "getaddrinfo"
#define IOTJS_MAGIC_STRING_HEXWRITE "hexWrite"
#define IOTJS_MAGIC_STRING_HIGH "HIGH"
#define IOTJS_MAGIC_STRING_HOME "HOME"
+#define IOTJS_MAGIC_STRING_HOST "host"
#define IOTJS_MAGIC_STRING_HTTPPARSER "HTTPParser"
#define IOTJS_MAGIC_STRING_IN "IN"
+#define IOTJS_MAGIC_STRING__INCOMING "_incoming"
#define IOTJS_MAGIC_STRING__INITARGV "_initArgv"
#define IOTJS_MAGIC_STRING_IOTJS_PATH "IOTJS_PATH"
#define IOTJS_MAGIC_STRING_IOTJS "iotjs"
#define IOTJS_MAGIC_STRING_IPV6 "IPv6"
#define IOTJS_MAGIC_STRING_ISALIVEEXCEPTFOR "isAliveExceptFor"
#define IOTJS_MAGIC_STRING_ISDEVUP "isDevUp"
+#define IOTJS_MAGIC_STRING_KEY "key"
#define IOTJS_MAGIC_STRING_LENGTH "length"
#define IOTJS_MAGIC_STRING_LISTEN "listen"
#define IOTJS_MAGIC_STRING_LOOPBACK "loopback"
#define IOTJS_MAGIC_STRING_NONE "NONE"
#define IOTJS_MAGIC_STRING_ONBODY "OnBody"
#define IOTJS_MAGIC_STRING_ONCLOSE "onclose"
+#define IOTJS_MAGIC_STRING_ONCLOSED "onClosed"
#define IOTJS_MAGIC_STRING_ONCONNECTION "onconnection"
+#define IOTJS_MAGIC_STRING_ONDATA "onData"
+#define IOTJS_MAGIC_STRING_ONEND "onEnd"
+#define IOTJS_MAGIC_STRING_ONERROR "onError"
#define IOTJS_MAGIC_STRING_ONHEADERSCOMPLETE "OnHeadersComplete"
#define IOTJS_MAGIC_STRING_ONHEADERS "OnHeaders"
#define IOTJS_MAGIC_STRING_ONMESSAGECOMPLETE "OnMessageComplete"
#define IOTJS_MAGIC_STRING_ONMESSAGE "onmessage"
#define IOTJS_MAGIC_STRING__ONNEXTTICK "_onNextTick"
#define IOTJS_MAGIC_STRING_ONREAD "onread"
+#define IOTJS_MAGIC_STRING_ONSOCKET "onSocket"
+#define IOTJS_MAGIC_STRING_ONTIMEOUT "onTimeout"
#define IOTJS_MAGIC_STRING__ONUNCAUGHTEXCEPTION "_onUncaughtException"
+#define IOTJS_MAGIC_STRING_ONWRITABLE "onWritable"
#define IOTJS_MAGIC_STRING_OPENDRAIN "OPENDRAIN"
#define IOTJS_MAGIC_STRING_OPEN "open"
#define IOTJS_MAGIC_STRING_OUT "OUT"
#define IOTJS_MAGIC_STRING_RISING_U "RISING"
#define IOTJS_MAGIC_STRING_RMDIR "rmdir"
#define IOTJS_MAGIC_STRING_SEND "send"
+#define IOTJS_MAGIC_STRING_SENDREQUEST "sendRequest"
#define IOTJS_MAGIC_STRING_SETADDRESS "setAddress"
#define IOTJS_MAGIC_STRING_SETBROADCAST "setBroadcast"
#define IOTJS_MAGIC_STRING_SETDUTYCYCLE "setDutyCycle"
#define IOTJS_MAGIC_STRING_SETMULTICASTLOOPBACK "setMulticastLoopback"
#define IOTJS_MAGIC_STRING_SETMULTICASTTTL "setMulticastTTL"
#define IOTJS_MAGIC_STRING_SETPERIOD "setPeriod"
+#define IOTJS_MAGIC_STRING_SETTIMEOUT "setTimeout"
#define IOTJS_MAGIC_STRING_SETTTL "setTTL"
#define IOTJS_MAGIC_STRING_SHOULDKEEPALIVE "shouldkeepalive"
#define IOTJS_MAGIC_STRING_SHUTDOWN "shutdown"
#define IOTJS_MAGIC_STRING_WRITESYNC "writeSync"
#define IOTJS_MAGIC_STRING_WRITEUINT8 "writeUInt8"
#define IOTJS_MAGIC_STRING_WRITE "write"
+#define IOTJS_MAGIC_STRING__WRITE "_write"
#endif /* IOTJS_STRING_CONSTANTS_H */
E(F, TCP, Tcp, tcp) \
E(F, TIMER, Timer, timer) \
E(F, UART, Uart, uart) \
- E(F, UDP, Udp, udp)
+ E(F, UDP, Udp, udp) \
+ E(F, HTTPS, Https, https)
#define ENUMDEF_MODULE_LIST(upper, Camel, lower) MODULE_##upper,
--- /dev/null
+/* Copyright 2017-present Samsung Electronics Co., Ltd. and other contributors
+ *
+ * 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.
+ */
+
+var client = require('https_client');
+
+var ClientRequest = exports.ClientRequest = client.ClientRequest;
+
+exports.request = function(options, cb) {
+ return new ClientRequest(options, cb);
+};
+
+exports.METHODS = client.METHODS;
+
+exports.get = function(options, cb) {
+ var req = exports.request(options, cb);
+ req.end();
+ return req;
+};
--- /dev/null
+/* Copyright 2017-present Samsung Electronics Co., Ltd. and other contributors
+ *
+ * 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.
+ */
+
+var util = require('util');
+var incoming = require('https_incoming');
+var stream = require('stream');
+var Buffer = require('buffer');
+var httpsNative = process.binding(process.binding.https);
+
+var methods = {'0': 'DELETE', '1': 'GET', '2': 'HEAD', '3': 'POST',
+ '4': 'PUT', '5': 'CONNECT', '6': 'OPTIONS', '7': 'TRACE'};
+exports.METHODS = methods;
+
+function ClientRequest(options, cb) {
+ this.stream = stream.Writable.call(this, options);
+
+ // get port, host and method.
+ var port = options.port = options.port || 443;
+ var host = options.host = options.hostname || options.host || '127.0.0.1';
+ var path = options.path || '/';
+ var protocol = options.protocol || 'https:';
+
+ this.host = protocol + '//' + host + ':' + port + path;
+ this.method = options.method || 'GET';
+ this.ca = options.ca || '';
+ this.cert = options.cert || '';
+ this.key = options.key || '';
+
+ var isMethodGood = false;
+ for (var key in methods) {
+ if (methods.hasOwnProperty(key)) {
+ if(this.method == methods[key]) {
+ isMethodGood = true;
+ }
+ }
+ }
+
+ if(!isMethodGood) {
+ var err = new Error('Incorrect options.method.')
+ this.emit('error', err);
+ return;
+ }
+
+ this._incoming = new incoming.IncomingMessage(this);
+ this._incoming.url = this.host;
+ this._incoming.method = this.method;
+ this.aborted = null;
+
+ // Register response event handler.
+ if (cb) {
+ this.once('response', cb);
+ }
+ this.once('finish', this.onFinish);
+
+ httpsNative.createRequest(this);
+
+ if (options.auth) {
+ var headerString = 'Authorization: Basic ' + toBase64(options.auth);
+ httpsNative.addHeader(headerString, this);
+ }
+ if (options.headers) {
+ var keys = Object.keys(options.headers);
+ for (var i = 0, l = keys.length; i < l; i++) {
+ var key = keys[i];
+ httpsNative.addHeader(key + ': ' + options.headers[key], this);
+ }
+ }
+ httpsNative.sendRequest(this);
+}
+
+util.inherits(ClientRequest, stream.Writable);
+
+// Concrete stream overriding the empty underlying _write method.
+ClientRequest.prototype._write = function(chunk, callback, onwrite) {
+ httpsNative._write(this, chunk.toString(), callback, onwrite);
+};
+
+ClientRequest.prototype.headersComplete = function() {
+ var self = this;
+ self.emit('response', self._incoming);
+ return (self.method == 'HEAD');
+};
+
+ClientRequest.prototype.onError = function(ret) {
+ this.emit('error', ret);
+};
+
+ClientRequest.prototype.onFinish = function() {
+ httpsNative.finishRequest(this);
+};
+
+ClientRequest.prototype.setTimeout = function(ms, cb) {
+ this._incoming.setTimeout(ms, cb);
+};
+
+ClientRequest.prototype.abort = function(doNotEmit) {
+ if (!this.aborted) {
+ httpsNative.abort(this);
+ var date = new Date();
+ this.aborted = date.getTime();
+
+ if (this._incoming.parser) {
+ this._incoming.parser.finish();
+ this._incoming.parser = null;
+ }
+
+ if (!doNotEmit) {
+ this.emit('abort');
+ }
+ }
+};
+
+exports.ClientRequest = ClientRequest;
+
+function toBase64(input) {
+ var output = '';
+ var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+ var i = 0;
+ //Convert to UTF-8
+ input = Buffer(input).toString();
+ var _keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +
+ '0123456789+/=';
+ while (i < input.length) {
+ chr1 = input.charCodeAt(i++);
+ chr2 = input.charCodeAt(i++);
+ chr3 = input.charCodeAt(i++);
+
+ enc1 = chr1 >> 2;
+ enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+ enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+ enc4 = chr3 & 63;
+
+ if (isNaN(chr2)) {
+ enc3 = enc4 = 64;
+ } else if (isNaN(chr3)) {
+ enc4 = 64;
+ }
+
+ output = output +
+ _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
+ _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
+ }
+ return output;
+}
--- /dev/null
+/* Copyright 2017-present Samsung Electronics Co., Ltd. and other contributors
+ *
+ * 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.
+ */
+
+var util = require('util');
+var stream = require('stream');
+var Buffer = require('buffer');
+var httpsNative = process.binding(process.binding.https);
+var HTTPParser = process.binding(process.binding.httpparser).HTTPParser;
+
+function IncomingMessage(clientRequest) {
+ stream.Readable.call(this);
+ this.clientRequest = clientRequest;
+
+ this.headers = {};
+ this.started = false;
+ this.completed = false;
+ this.timeoutCallback = null;
+ // for response (client)
+ this.statusCode = null;
+ this.statusMessage = null;
+ this.url = null;
+ this.method = null;
+
+ this.parser = createHTTPParser(this);
+
+ this.onData = cbOnData;
+ this.onClosed = cbOnClosed;
+ this.onEnd = cbOnEnd;
+ this.onError = cbOnError;
+ this.onSocket = cbOnSocket;
+ this.onTimeout = cbOnTimeout;
+ this.onWritable = cbOnWritable;
+}
+
+util.inherits(IncomingMessage, stream.Readable);
+exports.IncomingMessage = IncomingMessage;
+
+IncomingMessage.prototype.read = function(n) {
+ this.read = stream.Readable.prototype.read;
+ return this.read(n);
+};
+
+IncomingMessage.prototype.setTimeout = function(ms, cb) {
+ if (cb) {
+ this.timeoutCallback = cb;
+ this.once('timeout', cb);
+ }
+ httpsNative.setTimeout(ms, this.clientRequest);
+};
+
+IncomingMessage.prototype.addHeaders = function(headers) {
+ if (!this.headers) {
+ this.headers = {};
+ }
+ if (!headers) {
+ return;
+ }
+
+ for (var i = 0; i < headers.length; i = i + 2) {
+ this.headers[headers[i]] = headers[i + 1];
+ }
+};
+
+// HTTP PARSER Constructor
+var createHTTPParser = function(incoming) {
+ var parser = new HTTPParser(HTTPParser.RESPONSE);
+ parser.incoming = incoming;
+ // cb during http parsing from C side(http_parser)
+ parser.OnHeaders = parserOnHeaders;
+ parser.OnHeadersComplete = parserOnHeadersComplete;
+ parser.OnBody = parserOnBody;
+ parser.OnMessageComplete = parserOnMessageComplete;
+ return parser;
+};
+
+// ------------- HTTP PARSER CALLBACKS -------------
+// This is called when http header is fragmented and
+// HTTPParser sends it to JS in separate pieces.
+function parserOnHeaders(headers, url) {
+ var parser = this;
+ parser.incoming.addHeaders(headers);
+}
+
+// This is called when header part in http msg is parsed.
+function parserOnHeadersComplete(info) {
+ var parser = this;
+ var headers = info.headers;
+
+ if (!headers) {
+ headers = parser._headers;
+ parser.incoming.addHeaders(headers);
+ parser._headers = {};
+ } else {
+ parser.incoming.addHeaders(headers);
+ }
+
+ // add header fields of headers to incoming.headers
+ parser.incoming.addHeaders(headers);
+ parser.incoming.statusCode = info.status;
+ parser.incoming.statusMessage = info.status_msg;
+ parser.incoming.started = true;
+
+ // For client side, if response to 'HEAD' request, we will skip parsing body
+ return parser.incoming.clientRequest.headersComplete();
+}
+
+// parserOnBody is called when HTTPParser parses http msg(incoming) and
+// get body part(buf from start at length of len)
+function parserOnBody(buf, start, len) {
+ var parser = this;
+ var incoming = parser.incoming;
+
+ if (!incoming) {
+ return;
+ }
+
+ // Push body part into incoming stream, which will emit 'data' event
+ var body = buf.slice(start, start + len);
+ incoming.push(body);
+}
+
+// This is called when parsing of incoming http msg done
+function parserOnMessageComplete() {
+ var parser = this;
+ var incoming = parser.incoming;
+
+ if (incoming) {
+ incoming.completed = true;
+ // no more data from incoming, stream will emit 'end' event
+ incoming.push(null);
+ }
+}
+
+//------------ LIBCURL PARSER CALLBACKS -----------------
+// Called by libcurl when Request is Done. Finish parser and unref
+function cbOnEnd() {
+ var incoming = this;
+ var parser = incoming.parser;
+ if (parser) {
+ // unref all links to parser, make parser GCed
+ parser.finish();
+ parser = null;
+ incoming.parser = null;
+ }
+}
+
+function cbOnClosed() {
+ var incoming = this;
+ var parser = incoming.parser;
+ var clientRequest = incoming.clientRequest;
+
+ if (incoming.started && !incoming.completed) {
+ // Socket closed before we emitted 'end'
+ incoming.on('end', function() {
+ incoming.emit('close');
+ clientRequest.emit('close');
+ });
+ incoming.push(null);
+ } else if (!incoming.started) {
+ incoming.emit('close');
+ clientRequest.emit('close');
+ // socket closed before response starts.
+ var err = new Error('Could Not Start Connection');
+ clientRequest.onError(err);
+ } else {
+ clientRequest.emit('close');
+ }
+
+ if (parser) {
+ // unref all links to parser, make parser GCed
+ parser.finish();
+ parser = null;
+ incoming.parser = null;
+ }
+}
+
+// Called by libcurl when Request is Done. Finish parser and unref
+function cbOnData(chunk) {
+ var incoming = this;
+
+ if (!incoming) {
+ return false;
+ }
+
+ var parser = incoming.parser;
+ var clientRequest = incoming.clientRequest;
+
+ chunk = new Buffer(chunk);
+ var ret = parser.execute(chunk);
+
+ if (ret instanceof Error) {
+ parser.finish();
+ // unref all links to parser, make parser GCed
+ parser = null;
+ clientRequest.onError(ret);
+ return false;
+ }
+ return true;
+}
+
+function cbOnError(er) {
+ var incoming = this;
+ var clientRequest = incoming.clientRequest;
+ var err = new Error(er);
+ clientRequest.onError(err);
+ clientRequest.abort(true);
+ incoming.emit('error', err);
+}
+
+function cbOnTimeout() {
+ var incoming = this;
+ var clientRequest = incoming.clientRequest;
+ incoming.emit('timeout');
+ if (!incoming.timeoutCallback) {
+ clientRequest.abort.call(clientRequest, false);
+ }
+ incoming.emit('aborted');
+}
+
+function cbOnSocket() {
+ var incoming = this;
+ var clientRequest = incoming.clientRequest;
+ clientRequest.emit('socket');
+}
+
+function cbOnWritable() {
+ var incoming = this;
+ var clientRequest = incoming.clientRequest;
+ clientRequest._readyToWrite();
+}
--- /dev/null
+/* Copyright 2017-present Samsung Electronics Co., Ltd. and other contributors
+ *
+ * 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.
+ */
+
+#include "iotjs_module_https.h"
+#include "iotjs_objectwrap.h"
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdlib.h>
+#include <string.h>
+
+void iotjs_https_destroy(iotjs_https_t* https_data);
+IOTJS_DEFINE_NATIVE_HANDLE_INFO(https);
+
+//-------------Constructor------------
+iotjs_https_t* iotjs_https_create(const char* URL, const char* method,
+ const char* ca, const char* cert,
+ const char* key, const iotjs_jval_t* jthis) {
+ iotjs_https_t* https_data = IOTJS_ALLOC(iotjs_https_t);
+ IOTJS_VALIDATED_STRUCT_CONSTRUCTOR(iotjs_https_t, https_data);
+
+ // Original Request Details
+ _this->URL = URL;
+ _this->header_list = NULL;
+ if (strcmp(method, STRING_GET) == 0)
+ _this->method = HTTPS_GET;
+ else if (strcmp(method, STRING_POST) == 0)
+ _this->method = HTTPS_POST;
+ else if (strcmp(method, STRING_PUT) == 0)
+ _this->method = HTTPS_PUT;
+ else if (strcmp(method, STRING_DELETE) == 0)
+ _this->method = HTTPS_DELETE;
+ else if (strcmp(method, STRING_HEAD) == 0)
+ _this->method = HTTPS_HEAD;
+ else if (strcmp(method, STRING_CONNECT) == 0)
+ _this->method = HTTPS_CONNECT;
+ else if (strcmp(method, STRING_OPTIONS) == 0)
+ _this->method = HTTPS_OPTIONS;
+ else if (strcmp(method, STRING_TRACE) == 0)
+ _this->method = HTTPS_TRACE;
+ else {
+ // Will never reach here cuz checked in JS
+ }
+
+ // TLS certs stuff
+ _this->ca = ca;
+ _this->cert = cert;
+ _this->key = key;
+ // Content Length stuff
+ _this->content_length = -1;
+
+ // Handles
+ _this->loop = iotjs_environment_loop(iotjs_environment_get());
+ _this->jthis_native = iotjs_jval_create_copied(jthis);
+ iotjs_jval_set_object_native_handle(&(_this->jthis_native),
+ (uintptr_t)https_data,
+ &https_native_info);
+ _this->curl_multi_handle = curl_multi_init();
+ _this->curl_easy_handle = curl_easy_init();
+ _this->timeout.data = (void*)https_data;
+ uv_timer_init(_this->loop, &(_this->timeout));
+ _this->request_done = false;
+ _this->closing_handles = 3;
+ _this->poll_data = NULL;
+
+ // Timeout stuff
+ _this->timeout_ms = -1;
+ _this->last_bytes_num = -1;
+ _this->last_bytes_time = 0;
+ _this->socket_timeout.data = (void*)https_data;
+ uv_timer_init(_this->loop, &(_this->socket_timeout));
+
+ // ReadData stuff
+ _this->cur_read_index = 0;
+ _this->is_stream_writable = false;
+ _this->stream_ended = false;
+ _this->data_to_read = false;
+ _this->to_destroy_read_onwrite = false;
+ _this->async_read_onwrite.data = (void*)https_data;
+ uv_timer_init(_this->loop, &(_this->async_read_onwrite));
+ // No Need to read data for following types of requests
+ if (_this->method == HTTPS_GET || _this->method == HTTPS_DELETE ||
+ _this->method == HTTPS_HEAD || _this->method == HTTPS_OPTIONS ||
+ _this->method == HTTPS_TRACE)
+ _this->stream_ended = true;
+
+ return https_data;
+}
+
+// Destructor
+void iotjs_https_destroy(iotjs_https_t* https_data) {
+ IOTJS_VALIDATED_STRUCT_DESTRUCTOR(iotjs_https_t, https_data);
+ // To shutup unused variable _this warning
+ _this->URL = NULL;
+ IOTJS_RELEASE(https_data);
+}
+
+//----------------Utility Functions------------------
+void iotjs_https_check_done(iotjs_https_t* https_data) {
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ char* done_url;
+ CURLMsg* message;
+ int pending;
+ bool error = false;
+
+ while ((message = curl_multi_info_read(_this->curl_multi_handle, &pending))) {
+ switch (message->msg) {
+ case CURLMSG_DONE:
+ curl_easy_getinfo(message->easy_handle, CURLINFO_EFFECTIVE_URL,
+ &done_url);
+ break;
+ default:
+ error = true;
+ }
+ if (error) {
+ iotjs_jargs_t jarg = iotjs_jargs_create(1);
+ char error[] = "Unknown Error has occured.";
+ iotjs_string_t jresult_string =
+ iotjs_string_create_with_size(error, strlen(error));
+ iotjs_jargs_append_string(&jarg, &jresult_string);
+ iotjs_https_jcallback(https_data, IOTJS_MAGIC_STRING_ONERROR, &jarg,
+ false);
+ iotjs_string_destroy(&jresult_string);
+ iotjs_jargs_destroy(&jarg);
+ }
+ if (_this->stream_ended) {
+ iotjs_https_cleanup(https_data);
+ } else {
+ if (_this->to_destroy_read_onwrite) {
+ iotjs_https_call_read_onwrite_async(https_data);
+ }
+ _this->request_done = true;
+ }
+ break;
+ }
+}
+
+// Cleanup before destructor
+void iotjs_https_cleanup(iotjs_https_t* https_data) {
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ _this->loop = NULL;
+
+ uv_close((uv_handle_t*)&_this->timeout,
+ (uv_close_cb)iotjs_https_uv_close_callback);
+ uv_close((uv_handle_t*)&_this->socket_timeout,
+ (uv_close_cb)iotjs_https_uv_close_callback);
+ uv_close((uv_handle_t*)&_this->async_read_onwrite,
+ (uv_close_cb)iotjs_https_uv_close_callback);
+
+ iotjs_https_jcallback(https_data, IOTJS_MAGIC_STRING_ONEND,
+ iotjs_jargs_get_empty(), false);
+ iotjs_https_jcallback(https_data, IOTJS_MAGIC_STRING_ONCLOSED,
+ iotjs_jargs_get_empty(), false);
+
+ curl_multi_remove_handle(_this->curl_multi_handle, _this->curl_easy_handle);
+ curl_easy_cleanup(_this->curl_easy_handle);
+ _this->curl_easy_handle = NULL;
+ curl_multi_cleanup(_this->curl_multi_handle);
+ _this->curl_multi_handle = NULL;
+ curl_slist_free_all(_this->header_list);
+
+ if (_this->poll_data != NULL)
+ iotjs_https_poll_close_all(_this->poll_data);
+
+
+ if (_this->to_destroy_read_onwrite) {
+ const iotjs_jargs_t* jarg = iotjs_jargs_get_empty();
+ const iotjs_jval_t* jthis = &(_this->jthis_native);
+ IOTJS_ASSERT(iotjs_jval_is_function(&(_this->read_onwrite)));
+
+ if (!iotjs_jval_is_undefined(&(_this->read_callback)))
+ iotjs_make_callback(&(_this->read_callback), jthis, jarg);
+
+ iotjs_make_callback(&(_this->read_onwrite), jthis, jarg);
+ _this->to_destroy_read_onwrite = false;
+ iotjs_string_destroy(&(_this->read_chunk));
+ iotjs_jval_destroy(&(_this->read_onwrite));
+ iotjs_jval_destroy(&(_this->read_callback));
+ }
+ return;
+}
+
+CURLM* iotjs_https_get_multi_handle(iotjs_https_t* https_data) {
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ return _this->curl_multi_handle;
+}
+
+// Set various parameters of curl handles
+void iotjs_https_initialize_curl_opts(iotjs_https_t* https_data) {
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+
+ // Setup Some parameters for multi handle
+ curl_multi_setopt(_this->curl_multi_handle, CURLMOPT_SOCKETFUNCTION,
+ iotjs_https_curl_socket_callback);
+ curl_multi_setopt(_this->curl_multi_handle, CURLMOPT_SOCKETDATA,
+ (void*)https_data);
+ curl_multi_setopt(_this->curl_multi_handle, CURLMOPT_TIMERFUNCTION,
+ iotjs_https_curl_start_timeout_callback);
+ curl_multi_setopt(_this->curl_multi_handle, CURLMOPT_TIMERDATA,
+ (void*)https_data);
+
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_PROXY, "");
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_HEADERDATA,
+ (void*)https_data);
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_WRITEFUNCTION,
+ iotjs_https_curl_write_callback);
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_WRITEDATA,
+ (void*)https_data);
+
+ // Read and send data to server only for some request types
+ if (_this->method == HTTPS_POST || _this->method == HTTPS_PUT ||
+ _this->method == HTTPS_CONNECT) {
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_READFUNCTION,
+ iotjs_https_curl_read_callback);
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_READDATA,
+ (void*)https_data);
+ }
+
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_SOCKOPTFUNCTION,
+ iotjs_https_curl_sockopt_callback);
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_SOCKOPTDATA,
+ (void*)https_data);
+
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_URL, _this->URL);
+ _this->URL = NULL;
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_PROTOCOLS,
+ CURLPROTO_HTTP | CURLPROTO_HTTPS);
+
+ if (strlen(_this->ca) > 0)
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_CAINFO, _this->ca);
+ _this->ca = NULL;
+ if (strlen(_this->cert) > 0)
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_SSLCERT, _this->cert);
+ _this->cert = NULL;
+ if (strlen(_this->key) > 0)
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_SSLKEY, _this->key);
+ _this->key = NULL;
+
+ // Various request types
+ switch (_this->method) {
+ case HTTPS_GET:
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_HTTPGET, 1L);
+ break;
+ case HTTPS_POST:
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_POST, 1L);
+ break;
+ case HTTPS_PUT:
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_UPLOAD, 1L);
+ break;
+ case HTTPS_DELETE:
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_CUSTOMREQUEST,
+ "DELETE");
+ break;
+ case HTTPS_HEAD:
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_NOBODY, 1L);
+ break;
+ case HTTPS_CONNECT:
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_CUSTOMREQUEST,
+ "CONNECT");
+ break;
+ case HTTPS_OPTIONS:
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_CUSTOMREQUEST,
+ "OPTIONS");
+ break;
+ case HTTPS_TRACE:
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_CUSTOMREQUEST, "TRACE");
+ break;
+ }
+
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_HTTP_TRANSFER_DECODING, 0L);
+}
+
+// Get https.ClientRequest from struct
+iotjs_jval_t* iotjs_https_jthis_from_https(iotjs_https_t* https_data) {
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ return &(_this->jthis_native);
+}
+
+// Call any property of ClientRequest._Incoming
+bool iotjs_https_jcallback(iotjs_https_t* https_data, const char* property,
+ const iotjs_jargs_t* jarg, bool resultvalue) {
+ iotjs_jval_t* jthis = iotjs_https_jthis_from_https(https_data);
+ bool retval = true;
+ if (iotjs_jval_is_null(jthis))
+ return retval;
+
+ iotjs_jval_t jincoming =
+ iotjs_jval_get_property(jthis, IOTJS_MAGIC_STRING__INCOMING);
+ iotjs_jval_t cb = iotjs_jval_get_property(&jincoming, property);
+
+ IOTJS_ASSERT(iotjs_jval_is_function(&cb));
+ if (!resultvalue) {
+ iotjs_make_callback(&cb, &jincoming, jarg);
+ } else {
+ iotjs_jval_t result =
+ iotjs_make_callback_with_result(&cb, &jincoming, jarg);
+ retval = iotjs_jval_as_boolean(&result);
+ iotjs_jval_destroy(&result);
+ }
+
+ iotjs_jval_destroy(&jincoming);
+ iotjs_jval_destroy(&cb);
+ return retval;
+}
+
+// Call onWrite and callback after ClientRequest._write
+void iotjs_https_call_read_onwrite(uv_timer_t* timer) {
+ iotjs_https_t* https_data = (iotjs_https_t*)(timer->data);
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+
+ uv_timer_stop(&(_this->async_read_onwrite));
+ if (iotjs_jval_is_null(&_this->jthis_native))
+ return;
+ const iotjs_jargs_t* jarg = iotjs_jargs_get_empty();
+ const iotjs_jval_t* jthis = &(_this->jthis_native);
+ IOTJS_ASSERT(iotjs_jval_is_function(&(_this->read_onwrite)));
+
+ if (!iotjs_jval_is_undefined(&(_this->read_callback)))
+ iotjs_make_callback(&(_this->read_callback), jthis, jarg);
+
+ iotjs_make_callback(&(_this->read_onwrite), jthis, jarg);
+}
+
+// Call the above method Asynchronously
+void iotjs_https_call_read_onwrite_async(iotjs_https_t* https_data) {
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ uv_timer_start(&(_this->async_read_onwrite), iotjs_https_call_read_onwrite, 0,
+ 0);
+}
+
+// ------------Functions almost directly called by JS----------
+// Add a header to outgoing request
+void iotjs_https_add_header(iotjs_https_t* https_data,
+ const char* char_header) {
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ _this->header_list = curl_slist_append(_this->header_list, char_header);
+ if (_this->method == HTTPS_POST || _this->method == HTTPS_PUT) {
+ if (strncmp(char_header, "Content-Length: ", strlen("Content-Length: ")) ==
+ 0) {
+ const char* numberString = char_header + strlen("Content-Length: ");
+ _this->content_length = strtol(numberString, NULL, 10);
+ }
+ }
+}
+
+// Recieved data to write from ClientRequest._write
+void iotjs_https_data_to_write(iotjs_https_t* https_data,
+ iotjs_string_t read_chunk,
+ const iotjs_jval_t* callback,
+ const iotjs_jval_t* onwrite) {
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+
+ if (_this->to_destroy_read_onwrite) {
+ _this->to_destroy_read_onwrite = false;
+ iotjs_string_destroy(&(_this->read_chunk));
+ iotjs_jval_destroy(&(_this->read_onwrite));
+ iotjs_jval_destroy(&(_this->read_callback));
+ }
+
+ _this->read_chunk = read_chunk;
+ _this->data_to_read = true;
+
+ _this->read_callback = iotjs_jval_create_copied(callback);
+ _this->read_onwrite = iotjs_jval_create_copied(onwrite);
+ _this->to_destroy_read_onwrite = true;
+
+ if (_this->request_done) {
+ iotjs_https_call_read_onwrite_async(https_data);
+ } else if (_this->is_stream_writable) {
+ curl_easy_pause(_this->curl_easy_handle, CURLPAUSE_CONT);
+ uv_timer_stop(&(_this->timeout));
+ uv_timer_start(&(_this->timeout), iotjs_https_uv_timeout_callback, 1, 0);
+ }
+}
+
+// Finish writing all data from ClientRequest Stream
+void iotjs_https_finish_request(iotjs_https_t* https_data) {
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ _this->stream_ended = true;
+ if (_this->request_done) {
+ iotjs_https_cleanup(https_data);
+ } else if (_this->is_stream_writable) {
+ curl_easy_pause(_this->curl_easy_handle, CURLPAUSE_CONT);
+ uv_timer_stop(&(_this->timeout));
+ uv_timer_start(&(_this->timeout), iotjs_https_uv_timeout_callback, 1, 0);
+ }
+}
+
+// Start sending the request
+void iotjs_https_send_request(iotjs_https_t* https_data) {
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ // Add all the headers to the easy handle
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_HTTPHEADER,
+ _this->header_list);
+
+ if (_this->method == HTTPS_POST && _this->content_length != -1)
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_POSTFIELDSIZE,
+ _this->content_length);
+ else if (_this->method == HTTPS_PUT && _this->content_length != -1)
+ curl_easy_setopt(_this->curl_easy_handle, CURLOPT_INFILESIZE,
+ _this->content_length);
+
+ curl_multi_add_handle(_this->curl_multi_handle, _this->curl_easy_handle);
+}
+
+// Set timeout for request.
+void iotjs_https_set_timeout(long ms, iotjs_https_t* https_data) {
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ if (ms < 0)
+ return;
+ _this->timeout_ms = ms;
+ uv_timer_start(&(_this->socket_timeout),
+ iotjs_https_uv_socket_timeout_callback, 1, (uint64_t)ms);
+}
+
+
+//--------------CURL Callbacks------------------
+// Read callback is actually to write data to outgoing request
+size_t iotjs_https_curl_read_callback(void* contents, size_t size, size_t nmemb,
+ void* userp) {
+ iotjs_https_t* https_data = (iotjs_https_t*)userp;
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+
+ // If stream wasnt made writable yet, make it so.
+ if (!_this->is_stream_writable) {
+ _this->is_stream_writable = true;
+ iotjs_https_jcallback(https_data, IOTJS_MAGIC_STRING_ONWRITABLE,
+ iotjs_jargs_get_empty(), false);
+ }
+
+ if (_this->data_to_read) {
+ size_t real_size = size * nmemb;
+ size_t chunk_size = iotjs_string_size(&(_this->read_chunk));
+ size_t left_to_copy_size = chunk_size - _this->cur_read_index;
+
+ if (real_size < 1)
+ return 0;
+
+ // send some data
+ if (_this->cur_read_index < chunk_size) {
+ size_t num_to_copy =
+ (left_to_copy_size < real_size) ? left_to_copy_size : real_size;
+ const char* buf = iotjs_string_data(&(_this->read_chunk));
+ buf = &buf[_this->cur_read_index];
+ strncpy((char*)contents, buf, num_to_copy);
+ _this->cur_read_index = _this->cur_read_index + num_to_copy;
+ return num_to_copy;
+ }
+
+ // Finished sending one chunk of data
+ _this->cur_read_index = 0;
+ _this->data_to_read = false;
+ iotjs_https_call_read_onwrite_async(https_data);
+ }
+
+ // If the data is sent, and stream hasn't ended, wait for more data
+ if (!_this->stream_ended) {
+ return CURL_READFUNC_PAUSE;
+ }
+
+ // All done, end the transfer
+ return 0;
+}
+
+// Pass Curl events on its fd sockets
+int iotjs_https_curl_socket_callback(CURL* easy, curl_socket_t sockfd,
+ int action, void* userp, void* socketp) {
+ iotjs_https_t* https_data = (iotjs_https_t*)userp;
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ if (action == CURL_POLL_IN || action == CURL_POLL_OUT ||
+ action == CURL_POLL_INOUT) {
+ iotjs_https_poll_t* poll_data = NULL;
+
+ if (!socketp) {
+ poll_data = iotjs_https_poll_create(_this->loop, sockfd, https_data);
+ curl_multi_assign(_this->curl_multi_handle, sockfd, (void*)poll_data);
+ _this->closing_handles = _this->closing_handles + 1;
+ if (_this->poll_data == NULL)
+ _this->poll_data = poll_data;
+ else
+ iotjs_https_poll_append(_this->poll_data, poll_data);
+ } else
+ poll_data = (iotjs_https_poll_t*)socketp;
+
+ if (action == CURL_POLL_IN)
+ uv_poll_start(iotjs_https_poll_get_poll_handle(poll_data), UV_READABLE,
+ iotjs_https_uv_poll_callback);
+ else if (action == CURL_POLL_OUT)
+ uv_poll_start(iotjs_https_poll_get_poll_handle(poll_data), UV_WRITABLE,
+ iotjs_https_uv_poll_callback);
+ else if (action == CURL_POLL_INOUT)
+ uv_poll_start(iotjs_https_poll_get_poll_handle(poll_data),
+ UV_READABLE | UV_WRITABLE, iotjs_https_uv_poll_callback);
+ } else {
+ if (socketp) {
+ iotjs_https_poll_t* poll_data = (iotjs_https_poll_t*)socketp;
+ iotjs_https_poll_close(poll_data);
+ curl_multi_assign(_this->curl_multi_handle, sockfd, NULL);
+ }
+ }
+ return 0;
+}
+
+// Socket Assigned Callback
+int iotjs_https_curl_sockopt_callback(void* userp, curl_socket_t curlfd,
+ curlsocktype purpose) {
+ iotjs_https_t* https_data = (iotjs_https_t*)userp;
+ iotjs_https_jcallback(https_data, IOTJS_MAGIC_STRING_ONSOCKET,
+ iotjs_jargs_get_empty(), false);
+ return CURL_SOCKOPT_OK;
+}
+
+// Curl wants us to signal after timeout
+int iotjs_https_curl_start_timeout_callback(CURLM* multi, long timeout_ms,
+ void* userp) {
+ iotjs_https_t* https_data = (iotjs_https_t*)userp;
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ if (timeout_ms < 0)
+ uv_timer_stop(&(_this->timeout));
+ else {
+ if (timeout_ms == 0)
+ timeout_ms = 1;
+ if ((_this->timeout_ms != -1) && (timeout_ms > _this->timeout_ms))
+ timeout_ms = _this->timeout_ms;
+ uv_timer_start(&(_this->timeout), iotjs_https_uv_timeout_callback,
+ (uint64_t)timeout_ms, 0);
+ }
+ return 0;
+}
+
+// Write Callback is actually to read data from incoming response
+size_t iotjs_https_curl_write_callback(void* contents, size_t size,
+ size_t nmemb, void* userp) {
+ iotjs_https_t* https_data = (iotjs_https_t*)userp;
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ size_t real_size = size * nmemb;
+ if (iotjs_jval_is_null(&_this->jthis_native))
+ return real_size - 1;
+ iotjs_jargs_t jarg = iotjs_jargs_create(1);
+ iotjs_jval_t jresult_arr = iotjs_jval_create_byte_array(real_size, contents);
+ // iotjs_string_t jresult_string =
+ // iotjs_string_create_with_size(contents, real_size);
+ // iotjs_jargs_append_string(&jarg, &jresult_string);
+ // Use the jresult_arr Byte Array in production, but in testing use
+ // string. Uncomment out above line in testing.
+ iotjs_jargs_append_jval(&jarg, &jresult_arr);
+
+ bool result =
+ iotjs_https_jcallback(https_data, IOTJS_MAGIC_STRING_ONDATA, &jarg, true);
+
+ iotjs_jval_destroy(&jresult_arr);
+ // iotjs_string_destroy(&jresult_string);
+ iotjs_jargs_destroy(&jarg);
+
+ if (!result) {
+ return real_size - 1;
+ }
+
+ return real_size;
+}
+
+
+//--------------LibTUV Callbacks------------------
+// Callback called on closing handles during cleanup
+void iotjs_https_uv_close_callback(uv_handle_t* handle) {
+ iotjs_https_t* https_data = (iotjs_https_t*)handle->data;
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ _this->closing_handles = _this->closing_handles - 1;
+ if (_this->closing_handles <= 0) {
+ if (_this->poll_data != NULL)
+ iotjs_https_poll_destroy(_this->poll_data);
+ iotjs_jval_destroy(&_this->jthis_native);
+ }
+}
+
+// Callback called when poll detects actions on FD
+void iotjs_https_uv_poll_callback(uv_poll_t* poll, int status, int events) {
+ iotjs_https_poll_t* poll_data = (iotjs_https_poll_t*)poll->data;
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_poll_t, poll_data);
+ iotjs_https_t* https_data = (iotjs_https_t*)_this->https_data;
+
+ int flags = 0;
+ if (status < 0)
+ flags = CURL_CSELECT_ERR;
+ if (!status && events & UV_READABLE)
+ flags |= CURL_CSELECT_IN;
+ if (!status && events & UV_WRITABLE)
+ flags |= CURL_CSELECT_OUT;
+ int running_handles;
+ curl_multi_socket_action(iotjs_https_get_multi_handle(https_data),
+ _this->sockfd, flags, &running_handles);
+ iotjs_https_check_done(https_data);
+}
+
+// This function is for signalling to curl a given time has passed.
+// This timeout is usually given by curl itself.
+void iotjs_https_uv_timeout_callback(uv_timer_t* timer) {
+ iotjs_https_t* https_data = (iotjs_https_t*)(timer->data);
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ uv_timer_stop(timer);
+ curl_multi_socket_action(_this->curl_multi_handle, CURL_SOCKET_TIMEOUT, 0,
+ &_this->running_handles);
+ iotjs_https_check_done(https_data);
+}
+
+// Callback called to check if request has timed out
+void iotjs_https_uv_socket_timeout_callback(uv_timer_t* timer) {
+ iotjs_https_t* https_data = (iotjs_https_t*)(timer->data);
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_t, https_data);
+ double download_bytes = 0;
+ double upload_bytes = 0;
+ uint64_t total_time_ms = 0;
+
+ if (_this->timeout_ms != -1) {
+ curl_easy_getinfo(_this->curl_easy_handle, CURLINFO_SIZE_DOWNLOAD,
+ &download_bytes);
+ curl_easy_getinfo(_this->curl_easy_handle, CURLINFO_SIZE_UPLOAD,
+ &upload_bytes);
+ total_time_ms = uv_now(_this->loop);
+ double total_bytes = download_bytes + upload_bytes;
+
+ if (_this->last_bytes_num == total_bytes) {
+ if (total_time_ms >
+ ((uint64_t)_this->timeout_ms + _this->last_bytes_time)) {
+ if (!_this->request_done) {
+ iotjs_https_jcallback(https_data, IOTJS_MAGIC_STRING_ONTIMEOUT,
+ iotjs_jargs_get_empty(), false);
+ }
+ uv_timer_stop(&(_this->socket_timeout));
+ }
+ } else {
+ _this->last_bytes_num = total_bytes;
+ _this->last_bytes_time = total_time_ms;
+ }
+ }
+}
+
+//--------------https_poll Functions------------------
+iotjs_https_poll_t* iotjs_https_poll_create(uv_loop_t* loop,
+ curl_socket_t sockfd,
+ iotjs_https_t* https_data) {
+ iotjs_https_poll_t* poll_data = IOTJS_ALLOC(iotjs_https_poll_t);
+ IOTJS_VALIDATED_STRUCT_CONSTRUCTOR(iotjs_https_poll_t, poll_data);
+ _this->sockfd = sockfd;
+ _this->poll_handle.data = poll_data;
+ _this->https_data = https_data;
+ _this->closing = false;
+ _this->next = NULL;
+ uv_poll_init_socket(loop, &_this->poll_handle, sockfd);
+ return poll_data;
+}
+
+void iotjs_https_poll_append(iotjs_https_poll_t* head,
+ iotjs_https_poll_t* poll_data) {
+ iotjs_https_poll_t* current = head;
+ iotjs_https_poll_t* next = iotjs_https_poll_get_next(current);
+ while (next != NULL) {
+ current = next;
+ next = iotjs_https_poll_get_next(current);
+ }
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_poll_t, current);
+ _this->next = poll_data;
+}
+
+iotjs_https_poll_t* iotjs_https_poll_get_next(iotjs_https_poll_t* poll_data) {
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_poll_t, poll_data);
+ return _this->next;
+}
+
+uv_poll_t* iotjs_https_poll_get_poll_handle(iotjs_https_poll_t* poll_data) {
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_poll_t, poll_data);
+ return &_this->poll_handle;
+}
+
+void iotjs_https_poll_close(iotjs_https_poll_t* poll_data) {
+ IOTJS_VALIDATED_STRUCT_METHOD(iotjs_https_poll_t, poll_data);
+ if (_this->closing == false) {
+ _this->closing = true;
+ uv_poll_stop(&_this->poll_handle);
+ _this->poll_handle.data = _this->https_data;
+ uv_close((uv_handle_t*)&_this->poll_handle, iotjs_https_uv_close_callback);
+ }
+ return;
+}
+
+void iotjs_https_poll_close_all(iotjs_https_poll_t* head) {
+ iotjs_https_poll_t* current = head;
+ while (current != NULL) {
+ iotjs_https_poll_close(current);
+ current = iotjs_https_poll_get_next(current);
+ }
+}
+
+void iotjs_https_poll_destroy(iotjs_https_poll_t* poll_data) {
+ IOTJS_VALIDATED_STRUCT_DESTRUCTOR(iotjs_https_poll_t, poll_data);
+ if (_this->next != NULL) {
+ iotjs_https_poll_destroy(_this->next);
+ }
+ IOTJS_RELEASE(poll_data);
+}
+
+// ------------JHANDLERS----------------
+
+JHANDLER_FUNCTION(createRequest) {
+ DJHANDLER_CHECK_THIS(object);
+ DJHANDLER_CHECK_ARGS(1, object);
+
+ const iotjs_jval_t* jthis = JHANDLER_GET_ARG(0, object);
+
+ iotjs_jval_t jhost = iotjs_jval_get_property(jthis, IOTJS_MAGIC_STRING_HOST);
+ iotjs_string_t host = iotjs_jval_as_string(&jhost);
+ iotjs_jval_destroy(&jhost);
+
+ iotjs_jval_t jmethod =
+ iotjs_jval_get_property(jthis, IOTJS_MAGIC_STRING_METHOD);
+ iotjs_string_t method = iotjs_jval_as_string(&jmethod);
+ iotjs_jval_destroy(&jmethod);
+
+ iotjs_jval_t jca = iotjs_jval_get_property(jthis, IOTJS_MAGIC_STRING_CA);
+ iotjs_string_t ca = iotjs_jval_as_string(&jca);
+ iotjs_jval_destroy(&jca);
+
+ iotjs_jval_t jcert = iotjs_jval_get_property(jthis, IOTJS_MAGIC_STRING_CERT);
+ iotjs_string_t cert = iotjs_jval_as_string(&jcert);
+ iotjs_jval_destroy(&jcert);
+
+ iotjs_jval_t jkey = iotjs_jval_get_property(jthis, IOTJS_MAGIC_STRING_KEY);
+ iotjs_string_t key = iotjs_jval_as_string(&jkey);
+ iotjs_jval_destroy(&jkey);
+
+ if (curl_global_init(CURL_GLOBAL_SSL)) {
+ return;
+ }
+ iotjs_https_t* https_data =
+ iotjs_https_create(iotjs_string_data(&host), iotjs_string_data(&method),
+ iotjs_string_data(&ca), iotjs_string_data(&cert),
+ iotjs_string_data(&key), jthis);
+
+ iotjs_https_initialize_curl_opts(https_data);
+
+ iotjs_string_destroy(&host);
+ iotjs_string_destroy(&method);
+ iotjs_string_destroy(&ca);
+ iotjs_string_destroy(&cert);
+ iotjs_string_destroy(&key);
+ iotjs_jhandler_return_null(jhandler);
+}
+
+JHANDLER_FUNCTION(addHeader) {
+ DJHANDLER_CHECK_THIS(object);
+
+ DJHANDLER_CHECK_ARGS(2, string, object);
+ iotjs_string_t header = JHANDLER_GET_ARG(0, string);
+ const char* char_header = iotjs_string_data(&header);
+
+ const iotjs_jval_t* jthis = JHANDLER_GET_ARG(1, object);
+ iotjs_https_t* https_data =
+ (iotjs_https_t*)iotjs_jval_get_object_native_handle(jthis);
+ iotjs_https_add_header(https_data, char_header);
+
+ iotjs_string_destroy(&header);
+ iotjs_jhandler_return_null(jhandler);
+}
+
+JHANDLER_FUNCTION(sendRequest) {
+ DJHANDLER_CHECK_THIS(object);
+
+ DJHANDLER_CHECK_ARG(0, object);
+ const iotjs_jval_t* jthis = JHANDLER_GET_ARG(0, object);
+ iotjs_https_t* https_data =
+ (iotjs_https_t*)iotjs_jval_get_object_native_handle(jthis);
+ iotjs_https_send_request(https_data);
+ iotjs_jhandler_return_null(jhandler);
+}
+
+JHANDLER_FUNCTION(setTimeout) {
+ DJHANDLER_CHECK_THIS(object);
+ DJHANDLER_CHECK_ARGS(2, number, object);
+
+ double ms = JHANDLER_GET_ARG(0, number);
+ const iotjs_jval_t* jthis = JHANDLER_GET_ARG(1, object);
+
+ iotjs_https_t* https_data =
+ (iotjs_https_t*)iotjs_jval_get_object_native_handle(jthis);
+ iotjs_https_set_timeout((long)ms, https_data);
+
+ iotjs_jhandler_return_null(jhandler);
+}
+
+JHANDLER_FUNCTION(_write) {
+ DJHANDLER_CHECK_THIS(object);
+ DJHANDLER_CHECK_ARGS(2, object, string);
+ // Argument 3 can be null, so not checked directly, checked later
+ DJHANDLER_CHECK_ARG(3, function);
+
+ const iotjs_jval_t* jthis = JHANDLER_GET_ARG(0, object);
+ iotjs_string_t read_chunk = JHANDLER_GET_ARG(1, string);
+
+ const iotjs_jval_t* callback = iotjs_jhandler_get_arg(jhandler, 2);
+ const iotjs_jval_t* onwrite = JHANDLER_GET_ARG(3, function);
+
+ iotjs_https_t* https_data =
+ (iotjs_https_t*)iotjs_jval_get_object_native_handle(jthis);
+ iotjs_https_data_to_write(https_data, read_chunk, callback, onwrite);
+
+ // readchunk was copied to https_data, hence not destroyed.
+ iotjs_jhandler_return_null(jhandler);
+}
+
+JHANDLER_FUNCTION(finishRequest) {
+ DJHANDLER_CHECK_THIS(object);
+ DJHANDLER_CHECK_ARG(0, object);
+
+ const iotjs_jval_t* jthis = JHANDLER_GET_ARG(0, object);
+ iotjs_https_t* https_data =
+ (iotjs_https_t*)iotjs_jval_get_object_native_handle(jthis);
+ iotjs_https_finish_request(https_data);
+
+ iotjs_jhandler_return_null(jhandler);
+}
+
+JHANDLER_FUNCTION(Abort) {
+ DJHANDLER_CHECK_THIS(object);
+ DJHANDLER_CHECK_ARG(0, object);
+
+ const iotjs_jval_t* jthis = JHANDLER_GET_ARG(0, object);
+ iotjs_https_t* https_data =
+ (iotjs_https_t*)iotjs_jval_get_object_native_handle(jthis);
+ iotjs_https_cleanup(https_data);
+
+ iotjs_jhandler_return_null(jhandler);
+}
+
+iotjs_jval_t InitHttps() {
+ iotjs_jval_t https = iotjs_jval_create_object();
+
+ iotjs_jval_set_method(&https, IOTJS_MAGIC_STRING_CREATEREQUEST,
+ createRequest);
+ iotjs_jval_set_method(&https, IOTJS_MAGIC_STRING_ADDHEADER, addHeader);
+ iotjs_jval_set_method(&https, IOTJS_MAGIC_STRING_SENDREQUEST, sendRequest);
+ iotjs_jval_set_method(&https, IOTJS_MAGIC_STRING_SETTIMEOUT, setTimeout);
+ iotjs_jval_set_method(&https, IOTJS_MAGIC_STRING__WRITE, _write);
+ iotjs_jval_set_method(&https, IOTJS_MAGIC_STRING_FINISHREQUEST,
+ finishRequest);
+ iotjs_jval_set_method(&https, IOTJS_MAGIC_STRING_ABORT, Abort);
+
+ return https;
+}
--- /dev/null
+/* Copyright 2017-present Samsung Electronics Co., Ltd. and other contributors
+ *
+ * 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.
+ */
+
+#ifndef IOTJS_MODULE_HTTPS_H
+#define IOTJS_MODULE_HTTPS_H
+
+#include "iotjs_def.h"
+#include <uv.h>
+#include <curl/curl.h>
+
+typedef enum {
+ HTTPS_GET = 0,
+ HTTPS_POST,
+ HTTPS_PUT,
+ HTTPS_DELETE,
+ HTTPS_HEAD,
+ HTTPS_CONNECT,
+ HTTPS_OPTIONS,
+ HTTPS_TRACE
+} HTTPS_Methods;
+
+#define STRING_GET "GET"
+#define STRING_POST "POST"
+#define STRING_PUT "PUT"
+#define STRING_DELETE "DELETE"
+#define STRING_HEAD "HEAD"
+#define STRING_CONNECT "CONNECT"
+#define STRING_OPTIONS "OPTIONS"
+#define STRING_TRACE "TRACE"
+
+// A Per-Request Struct, native bound to https.ClientRequest
+typedef struct {
+ // Original Request Details
+ const char* URL;
+ HTTPS_Methods method;
+ struct curl_slist* header_list;
+ // TLS certs Options
+ const char* ca;
+ const char* cert;
+ const char* key;
+ // Content-Length for Post and Put
+ long content_length;
+
+ // Handles
+ uv_loop_t* loop;
+ iotjs_jval_t jthis_native;
+ CURLM* curl_multi_handle;
+ uv_timer_t timeout;
+ CURL* curl_easy_handle;
+ // Curl Context
+ int running_handles;
+ int closing_handles;
+ bool request_done;
+ struct iotjs_https_poll_t* poll_data;
+
+ // For SetTimeOut
+ uv_timer_t socket_timeout;
+ long timeout_ms;
+ double last_bytes_num;
+ uint64_t last_bytes_time;
+
+ // For Writable Stream ClientRequest
+ size_t cur_read_index;
+ bool is_stream_writable;
+ bool data_to_read;
+ bool stream_ended;
+ bool to_destroy_read_onwrite;
+ iotjs_string_t read_chunk;
+ iotjs_jval_t read_callback;
+ iotjs_jval_t read_onwrite;
+ uv_timer_t async_read_onwrite;
+
+} IOTJS_VALIDATED_STRUCT(iotjs_https_t);
+
+iotjs_https_t* iotjs_https_create(const char* URL, const char* method,
+ const char* ca, const char* cert,
+ const char* key, const iotjs_jval_t* jthis);
+
+#define THIS iotjs_https_t* https_data
+// Some utility functions
+void iotjs_https_check_done(THIS);
+void iotjs_https_cleanup(THIS);
+CURLM* iotjs_https_get_multi_handle(THIS);
+void iotjs_https_initialize_curl_opts(THIS);
+iotjs_jval_t* iotjs_https_jthis_from_https(THIS);
+bool iotjs_https_jcallback(THIS, const char* property,
+ const iotjs_jargs_t* jarg, bool resultvalue);
+void iotjs_https_call_read_onwrite(uv_timer_t* timer);
+void iotjs_https_call_read_onwrite_async(THIS);
+
+// Functions almost directly called by JS via JHANDLER
+void iotjs_https_add_header(THIS, const char* char_header);
+void iotjs_https_data_to_write(THIS, iotjs_string_t read_chunk,
+ const iotjs_jval_t* callback,
+ const iotjs_jval_t* onwrite);
+void iotjs_https_finish_request(THIS);
+void iotjs_https_send_request(THIS);
+void iotjs_https_set_timeout(long ms, THIS);
+#undef THIS
+
+
+// CURL callbacks
+size_t iotjs_https_curl_read_callback(void* contents, size_t size, size_t nmemb,
+ void* userp);
+int iotjs_https_curl_socket_callback(CURL* easy, curl_socket_t sockfd,
+ int action, void* userp, void* socketp);
+int iotjs_https_curl_sockopt_callback(void* userp, curl_socket_t curlfd,
+ curlsocktype purpose);
+int iotjs_https_curl_start_timeout_callback(CURLM* multi, long timeout_ms,
+ void* userp);
+size_t iotjs_https_curl_write_callback(void* contents, size_t size,
+ size_t nmemb, void* userp);
+
+// UV Callbacks
+void iotjs_https_uv_close_callback(uv_handle_t* handle);
+void iotjs_https_uv_poll_callback(uv_poll_t* poll, int status, int events);
+void iotjs_https_uv_socket_timeout_callback(uv_timer_t* timer);
+void iotjs_https_uv_timeout_callback(uv_timer_t* timer);
+
+typedef struct {
+ uv_poll_t poll_handle;
+ struct iotjs_https_poll_t* next;
+ struct iotjs_https_t* https_data;
+ curl_socket_t sockfd;
+ bool closing;
+} IOTJS_VALIDATED_STRUCT(iotjs_https_poll_t);
+
+iotjs_https_poll_t* iotjs_https_poll_create(uv_loop_t* loop,
+ curl_socket_t sockfd,
+ iotjs_https_t* https_data);
+void iotjs_https_poll_append(iotjs_https_poll_t* head,
+ iotjs_https_poll_t* poll_data);
+iotjs_https_poll_t* iotjs_https_poll_get_next(iotjs_https_poll_t* poll_data);
+uv_poll_t* iotjs_https_poll_get_poll_handle(iotjs_https_poll_t* poll_data);
+void iotjs_https_poll_close(iotjs_https_poll_t* poll_data);
+void iotjs_https_poll_destroy(iotjs_https_poll_t* poll_data);
+void iotjs_https_poll_close_all(iotjs_https_poll_t* head);
+
+#endif /* IOTJS_MODULE_HTTPS_H */
--- /dev/null
+/* Copyright 2017-present Samsung Electronics Co., Ltd. and other contributors
+ *
+ * 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.
+ */
+
+
+var assert = require('assert');
+var https = require('https');
+
+
+var isRequest1Finished = false;
+// 1. GET req
+options = {
+ method: 'GET',
+ host: "httpbin.org",
+ path: '/user-agent',
+ headers: {'user-agent': 'iotjs'}
+};
+
+var getResponseHandler = function (res) {
+ var res_body = '';
+
+ assert.equal(200, res.statusCode);
+
+ var endHandler = function(){
+ var response = JSON.parse(res_body);
+ assert.equal('iotjs', response['user-agent']);
+ isRequest1Finished = true;
+ };
+ res.on('end', endHandler);
+
+ res.on('data', function(chunk){
+ res_body += chunk.toString();
+ });
+};
+
+https.get(options, getResponseHandler);
+
+// 2. close server req
+var testMsg = 'Hello IoT.js';
+var finalOptions = {
+ method: 'POST',
+ host: "httpbin.org",
+ path: '/post',
+ headers: {'Content-Length': testMsg.length,
+ 'Content-Type': 'application/json'}
+};
+var isRequest2Finished = false;
+
+var finalResponseHandler = function (res) {
+ var res_body = '';
+
+ assert.equal(200, res.statusCode);
+
+ var endHandler = function(){
+ var response = JSON.parse(res_body);
+ assert.equal(testMsg, response['data']);
+ isRequest2Finished = true;
+ };
+ res.on('end', endHandler);
+
+ res.on('data', function(chunk){
+ res_body += chunk.toString();
+ });
+};
+
+var finalReq = https.request(finalOptions, finalResponseHandler);
+finalReq.write(testMsg);
+finalReq.end();
+
+
+process.on('exit', function() {
+ assert.equal(isRequest1Finished, true);
+ assert.equal(isRequest2Finished, true);
+});
--- /dev/null
+/* Copyright 2017-present Samsung Electronics Co., Ltd. and other contributors
+ *
+ * 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.
+ */
+
+var assert = require('assert');
+var http = require('http');
+var https = require('https');
+var net = require('net');
+
+// Messages for further requests.
+var message = 'Hello IoT.js';
+
+// Options for further requests.
+var options = {
+ method: 'POST',
+ host: "httpbin.org",
+ path: '/post',
+ headers: {'Content-Length': message.length,
+ 'Content-Type': 'application/json'}
+};
+
+// Simple request with valid utf-8 message.
+var isRequest1Finished = false;
+var request1 = https.request(options, function(response) {
+ var str = '';
+
+ response.on('data', function(chunk) {
+ str += chunk.toString();
+ });
+
+ response.on('end', function() {
+ var response = JSON.parse(str);
+ assert.equal(message, response['data']);
+ isRequest1Finished = true;
+ });
+});
+request1.end(message);
+
+
+// Simple request with multiple end callback.
+var isRequest2Finished = false;
+var request2 = https.request(options, function(response) {
+ var str = '';
+
+ response.on('data', function(chunk) {
+ str += chunk.toString();
+ });
+
+ response.on('end', function() {
+ var response = JSON.parse(str);
+ assert.equal(message, response['data']);
+ });
+});
+
+request2.end(message, function() {
+ isRequest2Finished = true;
+});
+
+// Call the request2 end again to test the finish state.
+request2.end(message, function() {
+ // This clabback should never be called.
+ assert.equal(isRequest2Finished, false);
+});
+
+
+// Simple request with buffer chunk as message parameter.
+var isRequest3Finished = false;
+var request3 = https.request(options, function(response) {
+ var str = '';
+
+ response.on('data', function(chunk) {
+ str += chunk;
+ });
+
+ response.on('end', function() {
+ var response = JSON.parse(str);
+ assert.equal(message, response['data']);
+ isRequest3Finished = true;
+ });
+});
+request3.end(new Buffer(message));
+
+
+// Test the IncomingMessage read function.
+var isRequest4Finished = false;
+var readRequest = https.request({
+ method: 'GET',
+ host: "httpbin.org",
+ path: '/get'
+});
+
+readRequest.on('response', function(incomingMessage) {
+ incomingMessage.on('readable', function() {
+ var inc = incomingMessage.read();
+ assert.equal(inc instanceof Buffer, true);
+ assert(inc.toString('utf8').length > 0);
+ isRequest4Finished = true;
+ });
+});
+readRequest.end();
+
+
+process.on('exit', function() {
+ assert.equal(isRequest1Finished, true);
+ assert.equal(isRequest2Finished, true);
+ assert.equal(isRequest3Finished, true);
+ assert.equal(isRequest4Finished, true);
+});
--- /dev/null
+/* Copyright 2015-present Samsung Electronics Co., Ltd. and other contributors
+ *
+ * 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.
+ */
+
+
+
+var assert = require('assert');
+var https = require('https');
+
+options = {
+ method: 'GET',
+ host: 'httpbin.org',
+ path: '/delay/10'
+};
+
+var getReq = https.get(options);
+
+getReq.on('error', function(){});
+
+var timeouted = false;
+getReq.setTimeout(5000, function() {
+ timeouted = true;
+ getReq.abort();
+});
+
+process.on('exit', function(code) {
+ assert.equal(timeouted, true);
+});
{ "name": "test_fs_open_read_sync_3.js", "skip": ["nuttx"], "reason": "not implemented for nuttx" },
{ "name": "test_gpio_input.js", "skip": ["all"], "reason": "needs hardware" },
{ "name": "test_gpio_output.js", "skip": ["all"], "reason": "need user input"},
+ { "name": "test_https_get.js", "timeout": 40 },
+ { "name": "test_https_request_response.js", "timeout": 40 },
+ { "name": "test_https_timeout.js", "timeout": 40 },
{ "name": "test_i2c.js", "skip": ["all"], "reason": "need to setup test environment" },
{ "name": "test_iotjs_promise.js", "skip": ["all"], "reason": "es2015 is off by default" },
{ "name": "test_module_cache.js", "skip": ["nuttx"], "reason": "not implemented for nuttx" },
if options.target_os:
system_os = options.target_os
- if system_os == 'tizen':
- system_os = 'linux'
build_modules_excludes |= set(
options.config['module']['exclude'][system_os])