Changeset 38533

Show
Ignore:
Timestamp:
10/01/10 09:54:32 (3 years ago)
Author:
anekos
Message:

chirpstream から userstream に変更。其れに伴い、nsIHTTPChannel を使うように変更

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • lang/javascript/vimperator-plugins/trunk/twittperator.js

    r38531 r38533  
    2929  <description>Twitter Client using ChirpStream</description> 
    3030  <description lang="ja">OAuth対応Twitterクライアント</description> 
    31   <version>1.7.3</version> 
     31  <version>1.8.0</version> 
    3232  <minVersion>2.3</minVersion> 
    3333  <maxVersion>2.4</maxVersion> 
     
    12221222 
    12231223  // Twittperator 
    1224   function Stream({ name, host, path }) { // {{{ 
    1225     let connectionInfo; 
     1224  function HTTPConnection(url, options) { // {{{ 
     1225    const ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); 
     1226 
     1227    this.events = {}; 
     1228 
     1229    this.channel = 
     1230      ioService.newChannelFromURI(ioService.newURI(url, null, null)).QueryInterface(Ci.nsIHttpChannel); 
     1231    this.channel.notificationCallbacks = this; 
     1232 
     1233    if (options) { 
     1234      if ("headers" in options) { 
     1235        for (let [n, v] in Iterator(options.headers)) 
     1236          this.channel.setRequestHeader(n, v, true); 
     1237      } 
     1238 
     1239      for (let [n, v] in Iterator(options)) { 
     1240        if (/^on[A-Z]/(n) && (v instanceof Function)) 
     1241          this.events[n.toLowerCase()] = v; 
     1242      } 
     1243 
     1244      this.onRecv = options.onReceive; 
     1245    } 
     1246 
     1247    this.channel.asyncOpen(this, null); 
     1248  } 
     1249  HTTPConnection.prototype = { 
     1250    callEvent: function(name) { 
     1251      name = name.toLowerCase(); 
     1252      if (name in this.events) 
     1253        return this.events[name].apply(this, Array.slice(arguments, 1)); 
     1254    }, 
     1255 
     1256    cancel: function() { 
     1257      if (this.channel) 
     1258        return this.channel.cancel(Cr.NS_ERROR_NOT_AVAILABLE); 
     1259    }, 
     1260 
     1261    // 実装しないと例外になるメソッドとか 
     1262    onStartRequest: function(request, context) {}, 
     1263    onProgress : function(request, context, progress, progressMax) {}, 
     1264    onStatus : function(request, context, status, statusArg) {}, 
     1265    onRedirect : function(oldChannel, newChannel) {}, 
     1266 
     1267    onDataAvailable: function(request, context, stream, sourceOffset, length) { 
     1268      let inputStream = 
     1269        Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream); 
     1270      inputStream.init(stream); 
     1271      let data = inputStream.read(length); 
     1272      this.callEvent("onReceive", data); 
     1273    }, 
     1274 
     1275    onStopRequest: function(request, context, status) { 
     1276      if (Components.isSuccessCode(status)) { 
     1277        this.callEvent("onComplete"); 
     1278      } else { 
     1279        this.callEvent("onError"); 
     1280      } 
     1281      delete this.channel; 
     1282    }, 
     1283 
     1284    onChannelRedirect: function(oldChannel, newChannel, flags) { 
     1285      this.channel = newChannel; 
     1286    }, 
     1287 
     1288    // nsIInterfaceRequestor 
     1289    getInterface: function(IID) { 
     1290      try { 
     1291        return this.QueryInterface(IID); 
     1292      } catch (e) { 
     1293        throw Components.results.NS_NOINTERFACE; 
     1294      } 
     1295    }, 
     1296 
     1297    // XPCOM インターフェイスに見せかけているので、QI を実装する必要がある 
     1298    QueryInterface : function(IID) { 
     1299      if (IID.equals(Ci.nsISupports) || 
     1300          IID.equals(Ci.nsIInterfaceRequestor) || 
     1301          IID.equals(Ci.nsIChannelEventSink) || 
     1302          IID.equals(Ci.nsIProgressEventSink) || 
     1303          IID.equals(Ci.nsIHttpEventSink) || 
     1304          IID.equals(Ci.nsIStreamListener)) 
     1305        return this; 
     1306 
     1307      throw Components.results.NS_NOINTERFACE; 
     1308    } 
     1309  }; // }}} 
     1310  function Stream({ name, url }) { // {{{ 
     1311    let connection; 
    12261312    let restartCount = 0; 
    1227     let startTime; 
    12281313    let lastParams; 
    1229     let lastReceivedTime; 
    12301314    let listeners = []; 
    12311315 
     
    12421326    function restart() { 
    12431327      stop(); 
    1244  
    12451328      if (restartCount > 13) 
    12461329        return liberator.echoerr("Twittperator: Gave up to connect to " + name + "..."); 
    1247  
    12481330      liberator.echoerr("Twittperator: " + name + " will be restared..."); 
    1249  
    12501331      // 試行済み回数^2 秒後にリトライ 
    12511332      setTimeout(function() start(lastParams), Math.pow(2, restartCount) * 1000); 
    1252  
    12531333      restartCount++; 
    12541334    } 
    12551335 
    12561336    function stop() { 
    1257       if (!connectionInfo) 
     1337      if (!connection) 
    12581338        return; 
    1259  
     1339      connection.cancel(); 
    12601340      liberator.log("Twittperator: stop " + name); 
    1261  
    1262       clearInterval(connectionInfo.interval); 
    1263       connectionInfo.sos.close(); 
    1264       connectionInfo.sis.close(); 
    1265  
    1266       connectionInfo = void 0; 
    12671341    } 
    12681342 
     
    12721346      liberator.log("Twittperator: start " + name); 
    12731347 
    1274       startTime = new Date().getTime(); 
    12751348      lastParams = params; 
    12761349 
    12771350      let useProxy = !!setting.proxyHost; 
    1278       let requestPath = path; 
     1351      let requestUrl = url; 
    12791352 
    12801353      if (params) { 
    12811354        // XXX Twitter がなぜか + を許容しない気がする(401 を返す)ので、再変換する 
    12821355        let query = tw.buildQuery(params).replace(/\+/g, "%20"); 
    1283         requestPath += '?' + query; 
    1284       } 
    1285  
    1286       let authHeader = tw.getAuthorizationHeader("http://" + host + requestPath); 
    1287  
    1288       if (useProxy) 
    1289         requestPath = "http://" + host + requestPath; 
    1290  
    1291       let get = [ 
    1292         "GET " + requestPath + " HTTP/1.1", 
    1293         "Host: " + host, 
    1294         "Authorization: " + authHeader, 
    1295         "Content-Type: application/x-www-form-urlencoded", 
    1296         "", 
    1297         "", 
    1298       ].join("\n"); 
    1299  
    1300       let socketService = 
    1301         let (stsvc = Cc["@mozilla.org/network/socket-transport-service;1"]) 
    1302           let (svc = stsvc.getService()) 
    1303             svc.QueryInterface(Ci["nsISocketTransportService"]); 
    1304  
    1305       let transport = 
    1306         socketService.createTransport( 
    1307           null, 0, 
    1308           useProxy ? setting.proxyHost : host, 
    1309           useProxy ? parseInt(setting.proxyPort || "3128", 10) : 80, 
    1310           null); 
    1311       let os = transport.openOutputStream(0, 0, 0); 
    1312       let is = transport.openInputStream(0, 0, 0); 
    1313       let sis = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream); 
    1314       let sos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream); 
    1315  
    1316       sis.init(is); 
    1317       sos.setOutputStream(os); 
    1318  
    1319       sos.write(get, get.length); 
    1320  
     1356        requestUrl += '?' + query; 
     1357      } 
     1358 
     1359      let authHeader = tw.getAuthorizationHeader(requestUrl); 
    13211360      let buf = ""; 
    1322       let first = true; 
    1323  
    1324       let interval = setInterval(function() { 
     1361 
     1362      let onReceive = function(data) { 
    13251363        try { 
    1326           let len = sis.available(); 
    1327           if (len <= 0) { 
    1328             // 30秒ごとにゴミデータを送ってくる仕様っぽいので、30x2+10秒 まつことにする。 
    1329             if (lastReceivedTime && ((new Date().getTime() - lastReceivedTime) > 70 * 1000)) { 
    1330               lastReceivedTime = 0; 
    1331               liberator.echoerr("Twittperator: " + name + " timed out"); 
    1332               restart(); 
    1333             } 
    1334             return; 
    1335           } 
    1336  
    1337           // 5分間接続されていたら、カウントをクリア 
    1338           // 何かの事情で即切断されてしまうときに、高頻度でアクセスしないための処置です。 
    1339           if (restartCount && (new Date().getTime() - startTime) > (5 * 60 * 1000)) 
    1340             restartCount = 0; 
    1341  
    1342           lastReceivedTime = new Date().getTime(); 
    1343           let data = sis.read(len); 
    13441364          let lines = data.split(/\r\n|[\r\n]/); 
    13451365          if (lines.length >= 2) { 
     
    13471367            for (let [, line] in Iterator(lines.slice(0, -1))) { 
    13481368              try { 
    1349                 if (first) { 
    1350                   first = false; 
    1351                   let [, code] = line.match(/^\S+\s+(\d+)/); 
    1352                   if (code != '200') { 
    1353                     stop(); 
    1354                     return liberator.echoerr("Twittperator: " + name + "'s  response code is " + code + "."); 
    1355                   } 
    1356                 } 
    13571369                if (/^\s*\{/(line)) 
    13581370                  onMsg(Utils.fixStatusObject(JSON.parse(line)), line); 
    1359                 else 
    1360                   liberator.log(name + ': \n' + line); 
    13611371              } catch (e) { liberator.log(e); } 
    13621372            } 
     
    13651375            buf += data; 
    13661376          } 
    1367         } catch (e if /^NS_(?:ERROR_NET_RESET|BASE_STREAM_CLOSED)$/(e)) { 
    1368           liberator.echoerr("Twittperator: " + name + " was stopped by " + e.name + "."); 
    1369           restart(); 
    1370           stop(); 
    13711377        } catch (e) { 
    13721378          liberator.echoerr("Twittperator: Unknown error on " + name + " connection: " + e.name); 
    1373           restart(); 
    1374           stop(); 
    1375         } 
    1376       }, 500); 
    1377  
    1378       connectionInfo = { 
    1379         sos: sos, 
    1380         sis: sis, 
    1381         interval: interval, 
    1382         params: params 
     1379        } 
    13831380      }; 
     1381 
     1382      connection = new HTTPConnection( 
     1383        requestUrl, 
     1384        { 
     1385          headers: { 
     1386            Authorization: authHeader, 
     1387          }, 
     1388          onReceive: onReceive, 
     1389          onError: restart 
     1390        } 
     1391      ); 
    13841392    } 
    13851393 
     
    18901898          } 
    18911899        }, 
    1892         completer: function (context, args) { 
     1900        completer: function(context, args) { 
    18931901          if (setting.trackWords) 
    18941902            context.completions = [[setting.trackWords, "Global variable"]]; 
     
    20942102 
    20952103  // ストリーム 
    2096   let ChirpUserStream = Stream({ name: 'chirp stream', host: "chirpstream.twitter.com", path: "/2b/user.json" }); 
    2097   let TrackingStream = Stream({ name: 'tracking stream', host: "stream.twitter.com", path: "/1/statuses/filter.json" }); 
     2104  let ChirpUserStream = Stream({ name: 'chirp stream', url: "https://userstream.twitter.com/2/user.json" }); 
     2105  let TrackingStream = Stream({ name: 'tracking stream', url: "http://stream.twitter.com/1/statuses/filter.json" }); 
    20982106 
    20992107  // 公開オブジェクト