All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.lab515.utils.WebServer Maven / Gradle / Ivy

There is a newer version: 1.0.12
Show newest version
package io.github.lab515.utils;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class WebServer implements HttpHandler {
  private String identifier = null;
  public WebServer(String identifier){
    this.identifier = identifier;
    addMe(this);
  }
  static WebServer[] _all = null;
  private static synchronized void addMe(WebServer ws){
    if(_all == null){
      _all = new WebServer[4];
      _all[0] = ws;
    }else{
      for(int i  =0; i < _all.length;i++){
        if(_all[i] == null){
          _all[i] = ws;
          return;
        }
      }
      _all = Arrays.copyOf(_all, _all.length*2);
      _all[_all.length/2] = ws;
    }
  }
  public static void stopAll(String filter){
    WebServer[] all = _all;
    ClassMatcher cm = filter  != null && (filter = filter.trim()).length() > 0 ? new ClassMatcher(filter.split("\n"),'.') : null;
    if(all != null){
      for(WebServer ws : all){
        if(ws != null){
          if(cm == null || (ws.identifier != null && cm.matchSingle(ws.identifier))) {
            ws.stop();
          }
        }
      }
    }
  }
  private class PathConfig{
    String pattern;
    ClassMatcher cm = null;
    String[] headers;
    Object resp; // assume no other response
    int status;
    WebFunc action;
    boolean includeQuery;
    boolean isFilter;
    boolean preOrPost;
    Object uo;
    PathConfig next;

    public PathConfig(String pattern, String[] headers, Object resp, int status, boolean isFilter, boolean preOrPost, Object uo){
      this.pattern = pattern;
      this.headers = headers;
      this.resp = resp;
      this.status = status;
      this.isFilter = isFilter;
      this.preOrPost = preOrPost;
      this.uo = uo;
      init();
    }

    public PathConfig(String pattern, WebFunc action, boolean isFilter, boolean preOrPost, Object uo){
      this.pattern = pattern;
      this.action = action;
      this.isFilter = isFilter;
      this.preOrPost = preOrPost;
      this.uo = uo;
      init();
    }
    private void init(){
      includeQuery = pattern.indexOf('?') >= 0;
      if(pattern.indexOf('*') >= 0){
        cm = new ClassMatcher(pattern.split("\n"),'/');
      }
    }

    public int match(String tar){
      if(cm != null)return cm.matchSingle(tar) ? 1 : 0;
      else{
        return tar.equals(pattern) ? 2 : 0;
      }
    }
  }
  private HashMap cachedConfigs = new HashMap<>();
  private HttpServer server = null;
  private PathConfig pathConfigs;


  private String getValidName(String name) {
    if (name != null) name = name.trim();//.toLowerCase()
    return (name == null || name.length() < 1) ? null : name;
  }

  private void addPathConfig(PathConfig pc){
    PathConfig cur = pathConfigs; // webHandler, beforeFilter, afterFilter
    PathConfig prev = null;
    while(cur != null){
      if(cur.isFilter != pc.isFilter){
        if(!cur.isFilter)break;
      }else if(cur.isFilter){
        if(cur.preOrPost != pc.preOrPost)break;
      }
      prev = cur;
      cur = cur.next;
    }
    if(prev == null){
      pc.next = pathConfigs;
      pathConfigs = pc;
    }else{
      cur = prev.next;
      prev.next = pc;
      pc.next = cur;
    }
  }

  public void addPathPreResponse(String path, String[] headers, Object response, int status){
    if(hasStarted())return;
    addPathConfig(new PathConfig(path, headers, response, status, true, true, null));
  }
  public void addPathPostResponse(String path, String[] headers, Object response, int status){
    if(hasStarted())return;
    addPathConfig(new PathConfig(path, headers, response, status, true, false,null));
  }
  public void addPathResponse(String path, String[] headers, Object response, int status){
    if(hasStarted())return;
    addPathConfig(new PathConfig(path, headers, response, status, false,true, null));
  }

  public void addPathPreFilter(String path, WebFunc filter, Object uo) {
    if(hasStarted())return;
    addPathConfig(new PathConfig(path, filter,true,true, uo));
  }

  public void addPathPostFilter(String path, WebFunc filter, Object uo) {
    if(hasStarted())return;
    addPathConfig(new PathConfig(path, filter,true,false, uo));
  }

  public void addPathHandler(String path, WebFunc handler, Object uo){
    if(hasStarted())return;
    addPathConfig(new PathConfig(path, handler,false,true, uo));
  }

  private static String getException(Throwable e){
    String ee = null;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    PrintStream pw = new PrintStream(baos);
    try {
      e.printStackTrace(pw);
      ee = baos.toString("utf-8");
    }catch (Exception e2){
      ee = "error writing exception: " + e.getMessage() + e.getClass().getName();
    }finally {
      try {
        baos.close();
        pw.close();
      }catch (Exception e2){}
    }
    return ee;
  }

  private void writeResponse(int status, HttpExchange exchange, Object response, List headers) throws IOException {
    //Headers rhdrs = exchange.getResponseHeaders();
    Map> nhdrs = new LinkedHashMap<>();
    boolean ctset = false;
    if (headers != null) {
      for(int i = 0; i < headers.size()-1;i+=2){
        String k = headers.get(i);
        String v = headers.get(i+1);
        List items = nhdrs.get(k);
        if(items == null)nhdrs.put(k, items = new ArrayList<>());
        items.add(v);
        if (!ctset) ctset = (k.equalsIgnoreCase("Content-Type"));
      }
    }
    if (!ctset) {
      ArrayList items = new ArrayList<>();
      items.add("text/plain");
      nhdrs.put("Content-Type", items);
    }
    exchange.getResponseHeaders().putAll(nhdrs);

    byte[] data = null;
    if(response != null){
      if(response instanceof byte[])data = (byte[])response;
      else data = response.toString().getBytes("utf-8");
    }
    exchange.sendResponseHeaders(status, data == null ? 0 : data.length);

    if (data != null) {
      try(OutputStream resp = exchange.getResponseBody()) {
        resp.write(data);
        resp.flush();
      }
    }else{
      exchange.close();
    }

  }

  public void handle(HttpExchange exchange) throws IOException {
    try {
      _handle(exchange);
    } catch (Exception e) {
      writeResponse(500, exchange, "Http Error During Processing: " + getException(e), null);
    }
  }
  private static HashMap getQuery(WebLoad resp) throws IOException{
    HashMap ret = new HashMap<>();
    String ct = resp.req_headers.get("content-type");
    String[] resolves = new String[2];
    if(ct != null && ct.toLowerCase().indexOf("application/x-www-form-urlencoded") >= 0){
      resolves[0] = resp.req_data;
    }
    if(resp.req_query != null && resp.req_query.length()> 0) {
      resolves[1] = resp.req_query;
    }
    for(String str : resolves){
      if(str == null || str.length() < 1)continue;
      for(String ar : str.split("&")) {
        int p = ar.indexOf('=');
        if (p < 1) continue;
        String n = ar.substring(0, p).trim();
        if (n.length() < 1) continue;
        String v = URLDecoder.decode(ar.substring(p + 1).trim(), "utf-8").trim();
        ret.put(n.toLowerCase(), v);
      }
    }
    return ret;
  }
  private WebLoad processRequest(HttpExchange exchange) throws IOException{
    WebLoad ret = new WebLoad();
    ret.req_method = exchange.getRequestMethod();// assume encoding only utf-8,
    ret.req_host = exchange.getRemoteAddress().getHostName();
    ret.req_host = exchange.getRemoteAddress().getAddress().getHostAddress();
    Headers hdrs = exchange.getRequestHeaders();
    StringBuilder sb = new StringBuilder();
    ret.req_headers = new HashMap<>();
    for(String key : hdrs.keySet()){
      for(String s : hdrs.get(key)){
        if(sb.length() > 0)sb.append(";");
        sb.append(s);
      }
      ret.req_headers.put(key.trim().toLowerCase(), sb.toString());
      sb.setLength(0);
    }
    try(InputStream ins = exchange.getRequestBody()) {
      try (ByteArrayOutputStream bas = new ByteArrayOutputStream()) {
        int len = 0;
        byte[] buf = new byte[8192];
        while ((len = ins.read(buf, 0, 8192)) > 0) {
          bas.write(buf, 0, len);
        }
        ret.req_data = new String(bas.toByteArray(), "utf-8");
      }
    }
    ret.req_url = exchange.getRequestURI().toString();
    int p = ret.req_url.indexOf('?');
    if(p > 0) {
      ret.req_query = ret.req_url.substring(p+1);
      ret.req_url = ret.req_url.substring(0,p);
    }else ret.req_query = "";
    ret.headers = new ArrayList<>();
    ret.url_params = getQuery(ret);
    return ret;
  }

  private WebLoad _handleLoad(PathConfig pc, WebLoad load) throws IOException{
    if (pc.action != null) {
      load.filtering = pc.isFilter;
      load.preOrPost = load.filtering ? pc.preOrPost : true;
      return pc.action.execute(load, pc.uo);
    } else {
      if (!pc.isFilter || load.status > 0) {
        load.status = pc.status;
      }
      if (pc.headers != null) {
        for (String aa : pc.headers) load.headers.add(aa);
      }
      if (pc.resp != null) {
        load.data = pc.resp;
      }
      return load;
    }
  }

  public void _handle(HttpExchange exchange) throws IOException {
    WebLoad load = processRequest(exchange);
    if (load.req_url.equalsIgnoreCase("/_shutdown")) {
      System.out.println("Trying to stop server...");
      writeResponse(200, exchange, "shutdown server is in progress now!", null);
      stopped = true; // prepare to shutdown
      return;
    }
    String full = load.req_url + load.req_query;
    WebLoad processed = null;
    WebLoad temp = null;
    PathConfig pc = pathConfigs;
    PathConfig target = null;
    int targetScore = 0;
    int score = 0;
    while(pc != null){
      if(pc.isFilter && target == null)break;
      if((score = pc.match(pc.includeQuery ? full : load.req_url)) > 0) {
        if (pc.isFilter) {
          if (!pc.preOrPost && processed == null) {
            processed = _handleLoad(pc, load);
            if (processed == null) {
              processed = load;
              break;
            }
            load = processed;
          }
          temp = _handleLoad(pc, load);
          if (temp == null) break;
          load = temp;
        } else {
          if (score > targetScore) {
            target = pc;
            targetScore = score;
          }
        }
      }
      pc = pc.next;
    }

    if(target == null){
      load.status = 404;
      load.data = "404 not found: good server, but not good enough!";
    }else{
      if(processed == null){
        processed = _handleLoad(target,load);
        if(processed != null)load = processed;
      }
      if(load.status < 0)load.status = 200;
    }
    writeResponse(load.status, exchange, load.data, load.headers);
  }

  private ExecutorService threadPool = null;
  private volatile boolean stopped = true;

  public boolean hasStarted() {
    return !stopped;
  }

  public void start(int port) throws IOException {
    if (threadPool != null) return;
    stopped = false;
    InetSocketAddress addr = new InetSocketAddress(port);
    server = HttpServer.create(addr, 0);
    server.createContext("/", this);
    server.setExecutor(threadPool = Executors.newCachedThreadPool());
    server.start();
  }

  public void stop() {
    if (server == null) return;
    server.stop(1);
    threadPool.shutdownNow();
    while (true) {
      try {
        if (threadPool.awaitTermination(1, TimeUnit.SECONDS)) break;
      } catch (InterruptedException e) {

      }
    }
    server = null;
    threadPool = null;
    stopped = true;
  }


  private boolean _sleep(int ms) {
    try {
      Thread.sleep(ms);
    } catch (InterruptedException e) {
    }
    return true;
  }

  public void loopTillStop(int ms) {
    while (!stopped && _sleep(ms)) {
      try {
        if (System.in.available() > 0) {
          if (System.in.read() == '\n') {
            break;
          }
        }
      } catch (IOException e) {

      }
    }
    System.out.println("Stopping...");
    stop();
    System.out.println("Stopped server completed");
  }

  public void loopTillStop() {
    loopTillStop(1000);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy