Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTPS support #100

Open
kevinresol opened this issue Jul 6, 2018 · 3 comments
Open

HTTPS support #100

kevinresol opened this issue Jul 6, 2018 · 3 comments

Comments

@kevinresol
Copy link
Member

Pretty sure someone will need it some day.

I suppose the API would be something like this:

var container = new SecureNodeContainer({
  port: ...,
  cert: ...,
  key: ...,
});

Should support client certificate too:
https://medium.com/@sevcsik/authentication-using-https-client-certificates-3c9d270e8326

@kevinresol
Copy link
Member Author

Leaving some note for potential implementors:

cert and key should be RealSource (PEM) and container.run() should resolve to Failed(e) if the said Source failed.

@varadig
Copy link

varadig commented Jul 1, 2019

What about this very needed feature?
I really need to use https instead of http, and I dont want to overlay nginx or something like this.

@varadig
Copy link

varadig commented Jul 1, 2019

I try hack like this:


package tink.http.containers;

import tink.http.Container;
import tink.http.Request;
import tink.http.Header;
import tink.io.*;
import js.node.http.*;

using tink.CoreApi;

class NodeContainer implements Container {
  
  var kind:ServerKind;
  var upgradable:Bool;
  
  public function new(kind:ServerKind, ?opt:{?upgradable:Bool}) {
    this.kind = kind;
    this.upgradable = opt != null && opt.upgradable;
  }
  
  static public function toNodeHandler(handler:Handler)
    return 
      function (req:IncomingMessage, res:ServerResponse)
        handler.process(
          new IncomingRequest(
            req.socket.remoteAddress, 
            IncomingRequestHeader.fromIncomingMessage(req),
            Plain(Source.ofNodeStream('Incoming HTTP message from ${req.socket.remoteAddress}', req)))
        ).handle(function (out) {
          res.writeHead(out.header.statusCode, out.header.reason, cast [for (h in out.header) [(h.name : String), h.value]]);//TODO: readable status code
          out.body.pipeTo(Sink.ofNodeStream('Outgoing HTTP response to ${req.socket.remoteAddress}', res)).handle(function (x) {
            res.end();
          });
        });
        
  static public function toUpgradeHandler(handler:Handler)
    return 
      function (req:js.node.http.IncomingMessage, socket:js.node.net.Socket, head:js.node.Buffer) {
        handler.process(
          new IncomingRequest(
            req.socket.remoteAddress, 
            IncomingRequestHeader.fromIncomingMessage(req),
            Plain(Source.ofNodeStream('Incoming HTTP message from ${req.socket.remoteAddress}', socket)))
        ).handle(function (out) {
          out.body.prepend(out.header.toString()).pipeTo(Sink.ofNodeStream('Outgoing HTTP response to ${req.socket.remoteAddress}', socket)).handle(function (_) {
            socket.end();
          });
        });
      }
  
  
  public function run(handler:Handler) 
    return Future.async(function (cb) {
      var failures = Signal.trigger();
      
      var server = switch kind {
        case InstanceSecure(server):
          server;
          
        case Instance(server):
          server;
          
        case Port(port):
          var server = new Server();
          server.listen(port);
          server;
          
        case Host(host):
          var server = new Server();
          server.listen(host.port, host.name);
          server;
          
        case Path(path):
          var server = new Server();
          server.listen(path);
          server;
          
        case Fd(fd):
          var server = new Server();
          server.listen(fd);
          server;
      }
      
      function tinkify(e:js.Error)
        return Error.withData(e.message, e);
        
      server.on('error', function (e) {
        cb(Failed(e));
      });
      
      if(upgradable)
        server.on('upgrade', toUpgradeHandler(handler));
      
      function onListen() {
        cb(Running({ 
          shutdown: function (hard:Bool) {
            if (hard)
              trace('Warning: hard shutdown not implemented');
              
            return Future.async(function (cb) {
              server.close(function () cb(true));
            });
          },
          failures: failures,//TODO: these need to be triggered
        }));
      }
      
      if(untyped server.listening) // .listening added in v5.7.0, not added to hxnodejs yet
        onListen()
      else
        server.on('listening', onListen);
      
      server.on('request', toNodeHandler(handler));
      server.on('error', function(e) cb(Failed(e)));
    });
}

private enum ServerKindBase {
  InstanceSecure(server:js.node.https.Server);
  Instance(server:Server);
  Port(port:Int);
  Host(host:tink.url.Host);
  Path(path:String);
  Fd(fd:{fd:Int});
}

abstract ServerKind(ServerKindBase) from ServerKindBase to ServerKindBase {
  @:from
  public static inline function fromInstanceSecure(server:js.node.https.Server):ServerKind
    return InstanceSecure(server);

  @:from
  public static inline function fromInstance(server:Server):ServerKind
    return Instance(server);
    
  @:from
  public static inline function fromPort(port:Int):ServerKind
    return Port(port);
  
  @:from
  public static inline function fromHost(host:tink.url.Host):ServerKind
    return Host(host);
  
  @:from
  public static inline function fromPath(path:String):ServerKind
    return Path(path);
  
  @:from
  public static inline function fromFd(fd:{fd:Int}):ServerKind
    return Fd(fd);
}

And use on this way:

		var options = {
			key: js.node.Fs.readFileSync('./ssl/server.key'),
			cert: js.node.Fs.readFileSync('./ssl/server.cert'),
			requestCert: false,
			rejectUnauthorized: false
		};

		this.httpsServer = js.node.Https.createServer(options);

		this.httpsServer.listen(443,function(){
			  log('Listening...');
		});
		this.container = new NodeContainer(this.httpsServer);

what you think, this would be a working solution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants