Package com.turn.ttorrent.common.protocol.http

Source Code of com.turn.ttorrent.common.protocol.http.HTTPAnnounceResponseMessage

/**
* Copyright (C) 2012 Turn, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.turn.ttorrent.common.protocol.http;

import com.turn.ttorrent.bcodec.BDecoder;
import com.turn.ttorrent.bcodec.BEValue;
import com.turn.ttorrent.bcodec.BEncoder;
import com.turn.ttorrent.bcodec.InvalidBEncodingException;
import com.turn.ttorrent.common.Peer;
import com.turn.ttorrent.common.Torrent;
import com.turn.ttorrent.common.protocol.TrackerMessage.AnnounceResponseMessage;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;


/**
* The announce response message from an HTTP tracker.
*
* @author mpetazzoni
*/
public class HTTPAnnounceResponseMessage extends HTTPTrackerMessage
  implements AnnounceResponseMessage {

  private final int interval;
  private final int complete;
  private final int incomplete;
  private final List<Peer> peers;

  private HTTPAnnounceResponseMessage(ByteBuffer data,
    int interval, int complete, int incomplete, List<Peer> peers) {
    super(Type.ANNOUNCE_RESPONSE, data);
    this.interval = interval;
    this.complete = complete;
    this.incomplete = incomplete;
    this.peers = peers;
  }

  @Override
  public int getInterval() {
    return this.interval;
  }

  @Override
  public int getComplete() {
    return this.complete;
  }

  @Override
  public int getIncomplete() {
    return this.incomplete;
  }

  @Override
  public List<Peer> getPeers() {
    return this.peers;
  }

  public static HTTPAnnounceResponseMessage parse(ByteBuffer data)
    throws IOException, MessageValidationException {
    BEValue decoded = BDecoder.bdecode(data);
    if (decoded == null) {
      throw new MessageValidationException(
        "Could not decode tracker message (not B-encoded?)!");
    }

    Map<String, BEValue> params = decoded.getMap();

    if (params.get("interval") == null) {
      throw new MessageValidationException(
        "Tracker message missing mandatory field 'interval'!");
    }

    try {
      List<Peer> peers;

      try {
        // First attempt to decode a compact response, since we asked
        // for it.
        peers = toPeerList(params.get("peers").getBytes());
      } catch (InvalidBEncodingException ibee) {
        // Fall back to peer list, non-compact response, in case the
        // tracker did not support compact responses.
        peers = toPeerList(params.get("peers").getList());
      }

      return new HTTPAnnounceResponseMessage(data,
        params.get("interval").getInt(),
        params.get("complete") != null ? params.get("complete").getInt() : 0,
        params.get("incomplete") != null ? params.get("incomplete").getInt() : 0,
        peers);
    } catch (InvalidBEncodingException ibee) {
      throw new MessageValidationException("Invalid response " +
        "from tracker!", ibee);
    } catch (UnknownHostException uhe) {
      throw new MessageValidationException("Invalid peer " +
        "in tracker response!", uhe);
    }
  }

  /**
   * Build a peer list as a list of {@link Peer}s from the
   * announce response's peer list (in non-compact mode).
   *
   * @param peers The list of {@link BEValue}s dictionaries describing the
   * peers from the announce response.
   * @return A {@link List} of {@link Peer}s representing the
   * peers' addresses. Peer IDs are lost, but they are not crucial.
   */
  private static List<Peer> toPeerList(List<BEValue> peers)
    throws InvalidBEncodingException {
    List<Peer> result = new LinkedList<Peer>();

    for (BEValue peer : peers) {
      Map<String, BEValue> peerInfo = peer.getMap();
      result.add(new Peer(
        peerInfo.get("ip").getString(Torrent.BYTE_ENCODING),
        peerInfo.get("port").getInt()));
    }

    return result;
  }

  /**
   * Build a peer list as a list of {@link Peer}s from the
   * announce response's binary compact peer list.
   *
   * @param data The bytes representing the compact peer list from the
   * announce response.
   * @return A {@link List} of {@link Peer}s representing the
   * peers' addresses. Peer IDs are lost, but they are not crucial.
   */
  private static List<Peer> toPeerList(byte[] data)
    throws InvalidBEncodingException, UnknownHostException {
    if (data.length % 6 != 0) {
      throw new InvalidBEncodingException("Invalid peers " +
        "binary information string!");
    }

    List<Peer> result = new LinkedList<Peer>();
    ByteBuffer peers = ByteBuffer.wrap(data);

    for (int i=0; i < data.length / 6 ; i++) {
      byte[] ipBytes = new byte[4];
      peers.get(ipBytes);
      InetAddress ip = InetAddress.getByAddress(ipBytes);
      int port =
        (0xFF & (int)peers.get()) << 8 |
        (0xFF & (int)peers.get());
      result.add(new Peer(new InetSocketAddress(ip, port)));
    }

    return result;
  }

  /**
   * Craft a compact announce response message.
   *
   * @param interval
   * @param minInterval
   * @param trackerId
   * @param complete
   * @param incomplete
   * @param peers
   */
  public static HTTPAnnounceResponseMessage craft(int interval,
    int minInterval, String trackerId, int complete, int incomplete,
    List<Peer> peers) throws IOException, UnsupportedEncodingException {
    Map<String, BEValue> response = new HashMap<String, BEValue>();
    response.put("interval", new BEValue(interval));
    response.put("complete", new BEValue(complete));
    response.put("incomplete", new BEValue(incomplete));

    ByteBuffer data = ByteBuffer.allocate(peers.size() * 6);
    for (Peer peer : peers) {
      byte[] ip = peer.getRawIp();
      if (ip == null || ip.length != 4) {
        continue;
      }
      data.put(ip);
      data.putShort((short)peer.getPort());
    }
    response.put("peers", new BEValue(data.array()));

    return new HTTPAnnounceResponseMessage(
      BEncoder.bencode(response),
      interval, complete, incomplete, peers);
  }
}
TOP

Related Classes of com.turn.ttorrent.common.protocol.http.HTTPAnnounceResponseMessage

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.