2014-06-25 124 views
0

尝试使用Nest API时,我得到了OAuth流程正常工作,进行了第一次API调用(至https://developer-api.nest.com/devices.json),按预期得到了307重定向,但随后调用了重定向位置以Remote host closed connection during handshake失败。我昨晚去了旧金山的Nest开发者大会,Lev Stesin告诉我在这里发布完整日志并提及他的名字。远程主机在与Nest API握手期间关闭连接

代码(APEX,在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的重定向:

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 

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

POST到https://api.home.nest.com/oauth2/access_token与身体

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

响应:(!我撤销令牌从home.nest.com,因此它是安全的,我在这里发表)

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

所以我做了GET on

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

和收到预期的307重定向,与位置

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

现在,当我得到的URL在Force.com上运行我的Apex代码,它失败

System.CalloutException: Remote host closed connection during handshake 

但如果我这样做在命令行上通过curl进行GET,它会成功,并返回预期的JSON响应。

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

编辑 - 下面是从卷曲-v输出到网址:

$ 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): 
+0

从详细卷曲请求到该URL的输出是什么? curl -v 此外,你可能想确保那些访问令牌无效:) – mimming

+0

添加(成功)curl -v输出到我的问题,并撤销访问令牌:) – metadaddy

回答

0

Salesforce的相同标注现在工作得很好。我猜Nest或Force.com必须调整了一些SSL配置。

1

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

+0

有趣的 - 如果我强制SSL/TLS版本与curl,只有**版本的作品是TLS v1.0('curl --tlsv1.0 ...')。 SSL v3,TLS v1.1或v1.2均不起作用。我会在我身边进一步调查。 – metadaddy

相关问题