使用Nest API进行握手时,远程主机关闭了连接 [英] Remote host closed connection during handshake with Nest API

查看:133
本文介绍了使用Nest API进行握手时,远程主机关闭了连接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

尝试使用Nest API,我使OAuth流程正常运行,进行了第一个API调用(对https://developer-api.nest.com/devices.json),按预期进行了307重定向,但是随后我对重定向位置的调用失败,并显示Remote host closed connection during handshake .昨晚我参加了在旧金山举行的Nest开发人员活动,并且 Lev Stesin 告诉我在此处发布完整日志并提及他的名字.

Trying out the Nest API, I got the OAuth flow working without problems, made the first API call (to https://developer-api.nest.com/devices.json), got the 307 redirect as expected, but then my call to the redirect location fails with Remote host closed connection during handshake. I went to the Nest developer event in San Francisco last night, and Lev Stesin told me to post a full log here and mention his name.

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

Code (Apex, running on Force.com):

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;
    }    
}

初始OAuth重定向:

Initial OAuth Redirect:

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

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

User authorizes app to access thermostats, Nest redirects back to my app:

https://c.na9.visual.force.com/apex/Nest?state=wow&code=6F3GV6WQ35NGLYB2

我成功地将代码交换为访问令牌:

I successfully exchange the code for an access token:

带有正文的POST到https://api.home.nest.com/oauth2/access_token

POST to https://api.home.nest.com/oauth2/access_token with body

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

响应:

{"access_token":"c.eDzTiwBeVak0Jq7RWVjBJPXrZT8kI5Hh4rgnYG7eDvzytZbqTJbMsnGBHLUcKOSZ7xjk8NR4oNAE4iUh1EBtkHllg55C0Ckb29jsSqL5VwdMxSUoTSBDkKt8QzMAoUCD3Ru8iSo7XYpPc8qU","expires_in":315360000}

(我从home.nest.com撤消了令牌,因此可以安全地在此处发布!)

(I revoked the token from home.nest.com, so it's safe for me to post here!)

所以我要去做

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

并收到预期的307重定向以及位置

and receive the expected 307 redirect, with location

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

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

Now, when I GET that URL in my Apex code running on Force.com, it fails with

System.CalloutException: Remote host closed connection during handshake

但是,如果我在命令行上通过curl执行相同的GET,它将成功,并返回预期的JSON响应.

But if I do the same GET from curl on the command line, it succeeds, returning the expected JSON response.

因此,看来SSL握手中可能存在一些不兼容.我将在Force.com端进行调查;如果Nest的某个人可以在末尾查看日志,那就太好了-这里应该有足够的详细信息.

So it looks like there may be some incompatibility in the SSL handshake. I'll investigate at the Force.com end; it would be good if someone at Nest could check the logs at their end - there should be enough detail here.

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

EDIT - Here's the output from curl -v to that URL:

$ curl -v 'https://firebase-apiserver01-tah01-iad01.dapi.production.nest.com:9553/devices.json?auth=c.dPHNEweWehQ47tzSm0sf13o8rX1isO9IdEG1HFwoAmeA2FtBLH1fTiksRtN9DGcPAOyEI3VINz2fD3CFma5ozSNbpqUIwGDGc8ixD1etjiIW6TmXN0Rd0p5VzEtk6sDwIe8j10NH1hKDhevX&print=pretty'
* About to connect() to firebase-apiserver01-tah01-iad01.dapi.production.nest.com port 9553 (#0)
*   Trying 54.196.205.148...
* connected
* Connected to firebase-apiserver01-tah01-iad01.dapi.production.nest.com (54.196.205.148) port 9553 (#0)
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server key exchange (12):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using EDH-RSA-DES-CBC3-SHA
* Server certificate:
*    subject: OU=Domain Control Validated; CN=*.dapi.production.nest.com
*    start date: 2014-05-28 22:31:28 GMT
*    expire date: 2015-05-28 22:31:28 GMT
*    subjectAltName: firebase-apiserver01-tah01-iad01.dapi.production.nest.com matched
*    issuer: C=US; ST=Arizona; L=Scottsdale; O=GoDaddy.com, Inc.; OU=http://certs.godaddy.com/repository/; CN=Go Daddy Secure Certificate Authority - G2
*    SSL certificate verify ok.
> GET /devices.json?auth=c.dPHNEweWehQ47tzSm0sf13o8rX1isO9IdEG1HFwoAmeA2FtBLH1fTiksRtN9DGcPAOyEI3VINz2fD3CFma5ozSNbpqUIwGDGc8ixD1etjiIW6TmXN0Rd0p5VzEtk6sDwIe8j10NH1hKDhevX&print=pretty HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8y zlib/1.2.5
> Host: firebase-apiserver01-tah01-iad01.dapi.production.nest.com:9553
> Accept: */*
> 
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=UTF-8
< Access-Control-Allow-Origin: *
< Cache-Control: private, no-cache, max-age=0
< Content-Length: 2218
< 
{
  "thermostats" : {
    "pYo-lbpXuVm_DctuTckA_HdEswRgRkbx" : {
      "locale" : "en-US",
      "temperature_scale" : "F",
      "is_using_emergency_heat" : false,
      "has_fan" : true,
      "software_version" : "4.2.3",
      "has_leaf" : true,
      "device_id" : "pYo-lbpXuVm_DctuTckA_HdEswRgRkbx",
      "name" : "Downstairs",
      "can_heat" : true,
      "can_cool" : true,
      "hvac_mode" : "off",
      "target_temperature_c" : 24.5,
      "target_temperature_f" : 76,
      "target_temperature_high_c" : 24.0,
      "target_temperature_high_f" : 75,
      "target_temperature_low_c" : 20.0,
      "target_temperature_low_f" : 68,
      "ambient_temperature_c" : 25.0,
      "ambient_temperature_f" : 78,
      "away_temperature_high_c" : 24.0,
      "away_temperature_high_f" : 76,
      "away_temperature_low_c" : 15.5,
      "away_temperature_low_f" : 60,
      "structure_id" : "HqSZlH08Jc3CtBNIS4OLPdiWLpcfW5o6dP2DvSox7hcGVpBGOH9cQA",
      "fan_timer_active" : false,
      "name_long" : "Downstairs Thermostat",
      "is_online" : true,
      "last_connection" : "2014-06-26T23:16:24.341Z"
    },
    "pYo-lbpXuVncrx7IdGTWyXdEswRgRkbx" : {
      "locale" : "en-US",
      "temperature_scale" : "F",
      "is_using_emergency_heat" : false,
      "has_fan" : true,
      "software_version" : "4.2.3",
      "has_leaf" : true,
      "device_id" : "pYo-lbpXuVncrx7IdGTWyXdEswRgRkbx",
      "name" : "Upstairs",
      "can_heat" : true,
      "can_cool" : true,
      "hvac_mode" : "off",
      "target_temperature_c" : 24.0,
      "target_temperature_f" : 76,
      "target_temperature_high_c" : 24.0,
      "target_temperature_high_f" : 75,
      "target_temperature_low_c" : 20.0,
      "target_temperature_low_f" : 68,
      "ambient_temperature_c" : 25.0,
      "ambient_temperature_f" : 78,
      "away_temperature_high_c" : 24.0,
      "away_temperature_high_f" : 76,
      "away_temperature_low_c" : 15.5,
      "away_temperature_low_f" : 60,
      "structure_id" : "HqSZlH08Jc3CtBNIS4OLPdiWLpcfW5o6dP2DvSox7hcGVpBGOH9cQA",
      "fan_timer_active" : false,
      "name_long" : "Upstairs Thermostat",
      "is_online" : true,
      "last_connection" : "2014-06-26T23:16:27.849Z"
    }
  }
* Connection #0 to host firebase-apiserver01-tah01-iad01.dapi.production.nest.com left intact
}* Closing connection #0
* SSLv3, TLS alert, Client hello (1):

推荐答案

Salesforce的相同标注现在可以正常工作了.我猜Nest或Force.com一定已经调整了一些SSL配置.

The same callout from Salesforce works just fine now. I guess Nest or Force.com must have tweaked some SSL config.

这篇关于使用Nest API进行握手时,远程主机关闭了连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆