与Nest API握手期间远程主机关闭连接

时间:2014-06-25 18:05:06

标签: apex force.com nest-api

尝试使用Nest API,我让OAuth流程没有问题,进行了第一次API调用(到https://developer-api.nest.com/devices.json),按预期获得了307重定向,但随后我对重定向位置的调用失败了{ {1}}。我昨晚去了旧金山的Nest开发者活动, Lev Stesin 告诉我在这里发一个完整的日志并提到他的名字。

代码(Apex,在Force.com上运行):

Remote host closed connection during handshake

初始OAuth重定向:

public with sharing virtual class NestController {
    public class OAuthResponse {
        public String access_token;
        public String token_type;
        public Integer expires_in;    
        public String refresh_token;
        public String error;
    }

    public static OAuthResponse parse(String json) {
        return (OAuthResponse) System.JSON.deserialize(json, OAuthResponse.class);
    }

    public String accessToken {get; set;}
    public String output {get; set;}

    private String getAll(String accessToken) {
        String url = 'https://developer-api.nest.com/devices.json?auth='+accessToken+'&print=pretty';

        HttpRequest req = new HttpRequest();
        req.setEndpoint(url);
        req.setMethod('GET');
        req.setTimeout(60*1000);

        Http h = new Http();
        String resp;
        HttpResponse res = h.send(req);
        resp = res.getBody();

        if (res.getStatusCode() == 307) {
            url = res.getHeader('Location');
            System.debug('Redirect to: '+url);

            req = new HttpRequest();
            req.setEndpoint(url);
            req.setMethod('GET');
            req.setTimeout(60*1000);

            h = new Http();
            res = h.send(req);
            resp = res.getBody();
        }

        System.debug('Get returns: '+resp);

        return resp;
    }

    public virtual PageReference login() {
        String clientId = '989360fb-9a1f-4d13-929e-0b40111c725a';
        String clientSecret = 'SECRET';
        String sessionId = null;
        String state = 'wow';

        // Get a URL for the page without any query params    
        String url = ApexPages.currentPage().getUrl().split('\\?')[0];

        System.debug('url is '+url);

        // note: connect url in fb application connect setting should be: https://c.na3.visual.force.com/apex/
        // you need the trailing slash even though it bitches about it
        String rediruri = 'https://'+ApexPages.currentPage().getHeaders().get('Host')+url;

        System.debug('rediruri is:'+rediruri);

        String authuri = 'https://home.nest.com/login/oauth2'+
            '?client_id='+clientId+
            '&state='+state;

        // No session
        PageReference pageRef;

        if (ApexPages.currentPage().getParameters().containsKey('error')) {
            // Initial step of OAuth - redirect to OAuth service
            System.debug('Error:' + ApexPages.currentPage().getParameters().get('error'));

            return null;
        }

        if (! ApexPages.currentPage().getParameters().containsKey('code')) {
            // Initial step of OAuth - redirect to OAuth service
            System.debug('Nest OAuth Step 1');

            return new PageReference(authuri);
        }

        // Second step of OAuth - get token from OAuth service
        String code = ApexPages.currentPage().getParameters().get('code');

        System.debug('Nest OAuth Step 2 - code:'+code);

        String tokenuri = 'https://api.home.nest.com/oauth2/access_token';
        String body = 'code='+code+
            '&client_id='+clientId+
            '&client_secret='+clientSecret+
            '&grant_type=authorization_code';
        System.debug('body is:'+body);

        HttpRequest req = new HttpRequest();
        req.setEndpoint(tokenuri);
        req.setMethod('POST');
        req.setTimeout(60*1000);
        req.setBody(body);

        Http h = new Http();
        String resp;
        if (code.equals('TEST')) {
            resp = 'access_token=TEST&expires=3600';
        } else {
            HttpResponse res = h.send(req);
            resp = res.getBody();
        }

        System.debug('FINAL RESP IS:'+resp);

        OAuthResponse oauth = parse(resp);

        if (oauth.error != null) {
            // Error getting token - probably reusing code - start again
            return new PageReference(authuri);            
        }

        accessToken = oauth.access_token;

        output = getAll(accessToken);

        return null;
    }    
}

用户授权应用访问恒温器,Nest重定向回我的应用程序:

https://home.nest.com/login/oauth2?client_id=989360fb-9a1f-4d13-929e-0b40111c725a&state=wow

我成功交换了访问令牌的代码:

使用正文

https://c.na9.visual.force.com/apex/Nest?state=wow&code=6F3GV6WQ35NGLYB2 发帖
https://api.home.nest.com/oauth2/access_token

响应:

code=6F3GV6WQ35NGLYB2&client_id=989360fb-9a1f-4d13-929e-0b40111c725a&client_secret=SECRET&grant_type=authorization_code

(我从{"access_token":"c.eDzTiwBeVak0Jq7RWVjBJPXrZT8kI5Hh4rgnYG7eDvzytZbqTJbMsnGBHLUcKOSZ7xjk8NR4oNAE4iUh1EBtkHllg55C0Ckb29jsSqL5VwdMxSUoTSBDkKt8QzMAoUCD3Ru8iSo7XYpPc8qU","expires_in":315360000} 撤销了令牌,所以我可以在这里发帖!)

所以我在

上做了一个GET
home.nest.com

并接收预期的307重定向,位置

https://developer-api.nest.com/devices.json?auth=c.eDzTiwBeVak0Jq7RWVjBJPXrZT8kI5Hh4rgnYG7eDvzytZbqTJbMsnGBHLUcKOSZ7xjk8NR4oNAE4iUh1EBtkHllg55C0Ckb29jsSqL5VwdMxSUoTSBDkKt8QzMAoUCD3Ru8iSo7XYpPc8qU&print=pretty

现在,当我在Force.com上运行的Apex代码中获取该URL时,它失败并带有

https://firebase-apiserver01-tah01-iad01.dapi.production.nest.com:9553/devices.json?auth=c.eDzTiwBeVak0Jq7RWVjBJPXrZT8kI5Hh4rgnYG7eDvzytZbqTJbMsnGBHLUcKOSZ7xjk8NR4oNAE4iUh1EBtkHllg55C0Ckb29jsSqL5VwdMxSUoTSBDkKt8QzMAoUCD3Ru8iSo7XYpPc8qU&print=pretty

但是如果我在命令行中从curl执行相同的GET,它会成功,返回预期的JSON响应。

因此看起来SSL握手可能存在一些不兼容性。我将在Force.com端调查;如果Nest的某个人可以在他们的最后检查日志会很好 - 这里应该有足够的细节。

编辑 - 这是curl -v到该网址的输出:

System.CalloutException: Remote host closed connection during handshake

2 个答案:

答案 0 :(得分:1)

我认为服务器不支持SSLv3。尝试使用--tlsv1,看看是否有效。

答案 1 :(得分:0)

Salesforce的相同标注现在运行正常。我猜Nest或Force.com必须调整一些SSL配置。