Package com.davfx.ninio.http

Source Code of com.davfx.ninio.http.HttpClient$Recycler

package com.davfx.ninio.http;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import com.davfx.ninio.common.Address;
import com.davfx.ninio.common.ByteBufferHandler;
import com.davfx.ninio.common.CloseableByteBufferHandler;
import com.davfx.ninio.common.OnceByteBufferAllocator;
import com.davfx.ninio.common.Queue;
import com.davfx.ninio.common.Ready;
import com.davfx.ninio.common.ReadyConnection;
import com.davfx.ninio.common.ReadyFactory;
import com.davfx.ninio.common.SocketReadyFactory;
import com.davfx.ninio.common.SslSocketReadyFactory;
import com.davfx.ninio.common.Trust;
import com.davfx.util.ConfigUtils;
import com.davfx.util.DateUtils;
import com.typesafe.config.Config;

public final class HttpClient implements AutoCloseable {
  private static final class Recycler {
    public HttpResponseReader reader;
    public HttpClientHandler handler;
    public CloseableByteBufferHandler write;
    public Date closeDate = null;
  }

  private static final Config CONFIG = ConfigUtils.load(HttpClient.class);
 
  private final Map<Address, Deque<Recycler>> recyclers = new HashMap<Address, Deque<Recycler>>();
  private final Queue queue;
  private ScheduledExecutorService recyclersCloserExecutorToClose;

  private final int defaultMaxRedirectLevels;
  private final double recyclersTimeToLive;

  private ReadyFactory readyFactory;
  private ReadyFactory secureReadyFactory;

  public HttpClient(Queue queue, Trust trust) {
    this(queue, trust, null);
  }
  public HttpClient(final Queue queue, Trust trust, ScheduledExecutorService recyclersCloserExecutor) {
    this.queue = queue;
   
    readyFactory = new SocketReadyFactory();
    secureReadyFactory = new SslSocketReadyFactory(trust);
   
    defaultMaxRedirectLevels = CONFIG.getInt("http.redirect.max.default");
    recyclersTimeToLive = ConfigUtils.getDuration(CONFIG, "http.recyclers.ttl");
   
    if (recyclersCloserExecutor == null) {
      recyclersCloserExecutor = Executors.newSingleThreadScheduledExecutor();
      recyclersCloserExecutorToClose = recyclersCloserExecutor;
    } else {
      recyclersCloserExecutorToClose = null;
    }
   
    recyclersCloserExecutor.scheduleAtFixedRate(new Runnable() {
      @Override
      public void run() {
        queue.post(new Runnable() {
          @Override
          public void run() {
            Date now = new Date();
            Iterator<Deque<Recycler>> recyclersIterator = recyclers.values().iterator();
            while (recyclersIterator.hasNext()) {
              Deque<Recycler> l = recyclersIterator.next();
              Iterator<Recycler> i = l.iterator();
              while (i.hasNext()) {
                Recycler r = i.next();
                if ((r.closeDate != null) && r.closeDate.before(now)) {
                  r.write.close();
                  i.remove();
                }
              }
              if (l.isEmpty()) {
                recyclersIterator.remove();
              }
            }
          }
        });
      }
    }, 0, (long) (ConfigUtils.getDuration(CONFIG, "http.recyclers.check") * 1000d), TimeUnit.MILLISECONDS);
  }
 
  public HttpClient override(ReadyFactory readyFactory) {
    this.readyFactory = readyFactory;
    return this;
  }
  public HttpClient overrideSecure(ReadyFactory readyFactory) {
    secureReadyFactory = readyFactory;
    return this;
  }
 
  @Override
  public void close() {
    if (recyclersCloserExecutorToClose != null) {
      recyclersCloserExecutorToClose.shutdown();
    }
  }
 
  public void send(HttpRequest request, HttpClientHandler clientHandler) {
    send(request, defaultMaxRedirectLevels, clientHandler);
  }
  public void send(final HttpRequest request, int maxRedirectLevels, HttpClientHandler clientHandler) {
    final HttpClientHandler handler = new RedirectHandler(maxRedirectLevels, 0, this, request, clientHandler);
    queue.post(new Runnable() {
      @Override
      public void run() {
        HttpResponseReader reader = new HttpResponseReader(handler);
        Deque<Recycler> oldRecyclers = recyclers.get(request.getAddress());
        if (oldRecyclers == null) {
          final Recycler newRecycler = new Recycler();
          newRecycler.reader = reader;
          newRecycler.handler = handler;
          newRecycler.closeDate = null;
          Ready ready;
          if (request.isSecure()) {
            ready = secureReadyFactory.create(queue, new OnceByteBufferAllocator());
          } else {
            ready = readyFactory.create(queue, new OnceByteBufferAllocator());
          }
          ready.connect(request.getAddress(), new ReadyConnection() {
            private HttpResponseReader.RecyclingHandler recyclingHandler;
            @Override
            public void handle(Address address, ByteBuffer buffer) {
              if (newRecycler.reader == null) {
                return;
              }
             
              newRecycler.reader.handle(buffer, recyclingHandler);
            }
           
            @Override
            public void failed(IOException e) {
              if (newRecycler.reader == null) {
                return;
              }
              newRecycler.reader.failed(e);
            }
           
            @Override
            public void connected(final CloseableByteBufferHandler write) {
              if (newRecycler.handler == null) {
                return;
              }
             
              recyclingHandler = new HttpResponseReader.RecyclingHandler() {
                @Override
                public void recycle() {
                  newRecycler.reader = null;
                  newRecycler.handler = null;
                  newRecycler.closeDate = DateUtils.from(DateUtils.from(new Date()) + recyclersTimeToLive);
                  Deque<Recycler> oldRecyclers = recyclers.get(request.getAddress());
                  if (oldRecyclers == null) {
                    oldRecyclers = new LinkedList<Recycler>();
                    recyclers.put(request.getAddress(), oldRecyclers);
                  }
                  oldRecyclers.add(newRecycler);
                }
                @Override
                public void close() {
                  write.close();
                }
              };

              newRecycler.write = write;
              write.handle(null, createRequest(request));
              newRecycler.handler.ready(write);
            }
           
            @Override
            public void close() {
              if (newRecycler.reader == null) {
                return;
              }
              newRecycler.reader.close();
            }
          });
        } else {
          Recycler oldRecycler = oldRecyclers.removeFirst();
          if (oldRecyclers.isEmpty()) {
            recyclers.remove(request.getAddress());
          }
          oldRecycler.reader = reader;
          oldRecycler.handler = handler;
          oldRecycler.closeDate = null;
          oldRecycler.write.handle(null, createRequest(request));
          oldRecycler.handler.ready(oldRecycler.write);
        }
      }
    });
  }
 
  private static void appendHeader(StringBuilder buffer, String key, String value) {
    buffer.append(key).append(Http.HEADER_KEY_VALUE_SEPARATOR).append(Http.HEADER_BEFORE_VALUE).append(value).append(Http.CR).append(Http.LF);
  }
  private static ByteBuffer createRequest(HttpRequest request) {
    StringBuilder header = new StringBuilder();
    header.append(request.getMethod().toString()).append(Http.START_LINE_SEPARATOR).append(request.getPath()).append(Http.START_LINE_SEPARATOR).append(Http.HTTP11).append(Http.CR).append(Http.LF);
   
    Map<String, String> headers = request.getHeaders();
   
    for (Map.Entry<String, String> h : headers.entrySet()) {
      appendHeader(header, h.getKey(), h.getValue());
    }
    if (!headers.containsKey(Http.HOST)) {
      appendHeader(header, Http.HOST, request.getAddress().getHost() + Http.PORT_SEPARATOR + request.getAddress().getPort());
    }
    if (!headers.containsKey(Http.ACCEPT_ENCODING)) {
      appendHeader(header, Http.ACCEPT_ENCODING, Http.GZIP);
    }
   
    header.append(Http.CR).append(Http.LF);
    return ByteBuffer.wrap(header.toString().getBytes(Http.USASCII_CHARSET));
  }
 
  private static class RedirectHandler implements HttpClientHandler {
    private final int maxRedirectLevels;
    private final int levelOfRedirect;
    private final HttpClient client;
    private final HttpRequest request;
    private final HttpClientHandler wrappee;
    private boolean redirected = false;
   
    public RedirectHandler(int maxRedirectLevels, int levelOfRedirect, HttpClient client, HttpRequest request, HttpClientHandler wrappee) {
      this.maxRedirectLevels = maxRedirectLevels;
      this.levelOfRedirect = levelOfRedirect;
      this.client = client;
      this.request = request;
      this.wrappee = wrappee;
    }
   
    @Override
    public void failed(IOException e) {
      if (redirected) {
        return;
      }
      wrappee.failed(e);
    }
    @Override
    public void finished() {
      if (redirected) {
        return;
      }
      wrappee.finished();
    }
   
    @Override
    public void received(HttpResponse response) {
      try {
        if (levelOfRedirect < maxRedirectLevels) {
          String location = response.getHeaders().get(Http.LOCATION);
          if (location != null) {
            Address newAddress = request.getAddress();
            String newPath;
            String l = null;
            boolean secure = false;
            int defaultPort = Http.DEFAULT_PORT;
            if (location.startsWith(Http.SECURE_PROTOCOL)) {
              l = location.substring(Http.SECURE_PROTOCOL.length());
              secure = true;
              defaultPort = Http.DEFAULT_SECURE_PORT;
            } else if (location.startsWith(Http.PROTOCOL)) {
              l = location.substring(Http.PROTOCOL.length());
            }
            if (l != null) {
              int i = l.indexOf(Http.PATH_SEPARATOR);
              if (i > 0) {
                newPath = l.substring(i);
                l = l.substring(0, i);
              } else {
                newPath = String.valueOf(Http.PATH_SEPARATOR);
              }
              int j = l.indexOf(Http.PORT_SEPARATOR);
              if (j < 0) {
                newAddress = new Address(l, defaultPort);
              } else {
                int newPort;
                String portValue = l.substring(j + 1);
                if (portValue.isEmpty()) {
                  newPort = defaultPort;
                } else {
                  try {
                    newPort = Integer.parseInt(portValue);
                  } catch (NumberFormatException e) {
                    throw new IOException("Bad location: " + location);
                  }
                }
                newAddress = new Address(l.substring(0, j), newPort);
              }
            } else {
              newPath = location;
            }
           
            redirected = true;
           
            HttpRequest newRequest = new HttpRequest(newAddress, secure, request.getMethod(), newPath);
            client.send(newRequest, new RedirectHandler(maxRedirectLevels, levelOfRedirect + 1, client, newRequest, wrappee));
            return;
          }
        }
        wrappee.received(response);
      } catch (IOException e) {
        wrappee.failed(e);
      }
    }
   
    @Override
    public void received(ByteBuffer buffer) {
      if (redirected) {
        return;
      }
      wrappee.received(buffer);
    }
   
    @Override
    public void ready(ByteBufferHandler write) {
      if (redirected) {
        return;
      }
      wrappee.ready(write);
    }
  }

}
TOP

Related Classes of com.davfx.ninio.http.HttpClient$Recycler

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.
s.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');