HTTPS Module for Tizen. 83/133683/2
authorAkhil Kedia <akhil.kedia@samsung.com>
Tue, 13 Jun 2017 06:20:26 +0000 (15:20 +0900)
committerSumin Lim <sumin.lim@samsung.com>
Tue, 13 Jun 2017 06:45:32 +0000 (06:45 +0000)
IoT.js-DCO-1.0-Signed-off-by: Akhil Kedia akhil.kedia@samsung.com

Change-Id: I75c80c5c50a91f42caacdcdd46f1619230aa5b12

14 files changed:
build.config
docs/api/IoT.js-API-HTTPS.md [new file with mode: 0644]
src/iotjs_magic_strings.h
src/iotjs_module.h
src/js/https.js [new file with mode: 0755]
src/js/https_client.js [new file with mode: 0644]
src/js/https_incoming.js [new file with mode: 0644]
src/modules/iotjs_module_https.c [new file with mode: 0644]
src/modules/iotjs_module_https.h [new file with mode: 0644]
test/run_pass/test_https_get.js [new file with mode: 0644]
test/run_pass/test_https_request_response.js [new file with mode: 0644]
test/run_pass/test_https_timeout.js [new file with mode: 0644]
test/testsets.json
tools/module_analyzer.py

index 342c82e01edd1b36858433088e7f51dff5744433..034cf4649937d5205b0ff705eb66d3e730270a5f 100644 (file)
       "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"]
     }
   }
 }
diff --git a/docs/api/IoT.js-API-HTTPS.md b/docs/api/IoT.js-API-HTTPS.md
new file mode 100644 (file)
index 0000000..5ed3aae
--- /dev/null
@@ -0,0 +1,133 @@
+### 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.
index 3334073fe15ea2c9da5dc547d593176b17be5390..89c9e20918b580bc70e6ae83a2fa694bafa8d635 100644 (file)
@@ -20,7 +20,9 @@
 #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"
@@ -41,6 +43,8 @@
 #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"
@@ -53,6 +57,7 @@
 #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"
@@ -74,6 +79,7 @@
 #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 */
index fb880b10d3c2d5309747cad7d0aabe6b84830672..73b10ad956bb358a15e0f2e730a0a08f5543c7a2 100644 (file)
@@ -52,7 +52,8 @@ typedef iotjs_jval_t (*register_func)();
   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,
 
diff --git a/src/js/https.js b/src/js/https.js
new file mode 100755 (executable)
index 0000000..7719092
--- /dev/null
@@ -0,0 +1,30 @@
+/* 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;
+};
diff --git a/src/js/https_client.js b/src/js/https_client.js
new file mode 100644 (file)
index 0000000..02246a6
--- /dev/null
@@ -0,0 +1,156 @@
+/* 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;
+}
diff --git a/src/js/https_incoming.js b/src/js/https_incoming.js
new file mode 100644 (file)
index 0000000..9570582
--- /dev/null
@@ -0,0 +1,242 @@
+/* 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();
+}
diff --git a/src/modules/iotjs_module_https.c b/src/modules/iotjs_module_https.c
new file mode 100644 (file)
index 0000000..1a0a9c3
--- /dev/null
@@ -0,0 +1,862 @@
+/* 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;
+}
diff --git a/src/modules/iotjs_module_https.h b/src/modules/iotjs_module_https.h
new file mode 100644 (file)
index 0000000..65f8965
--- /dev/null
@@ -0,0 +1,151 @@
+/* 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 */
diff --git a/test/run_pass/test_https_get.js b/test/run_pass/test_https_get.js
new file mode 100644 (file)
index 0000000..3ef93e2
--- /dev/null
@@ -0,0 +1,85 @@
+/* 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);
+});
diff --git a/test/run_pass/test_https_request_response.js b/test/run_pass/test_https_request_response.js
new file mode 100644 (file)
index 0000000..5054afd
--- /dev/null
@@ -0,0 +1,119 @@
+/* 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);
+});
diff --git a/test/run_pass/test_https_timeout.js b/test/run_pass/test_https_timeout.js
new file mode 100644 (file)
index 0000000..465fa24
--- /dev/null
@@ -0,0 +1,39 @@
+/* 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);
+});
index 44b8063c3d14376002d683225e5c910dd231c1ea..0d43ffe5ed8335d220039c41d20fb5d2765728ec 100644 (file)
@@ -41,6 +41,9 @@
     { "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" },
index 1da91a00501b88d5887ec13f98b7df589d1309ad..b051268fd12b4e2ac4c69cdc0ec8c8d5d56f8dac 100644 (file)
@@ -34,8 +34,6 @@ def resolve_modules(options):
 
     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])