RequestPlan.java

279 lines | 9.656 kB Blame History Raw Download
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufrgs.inf.prosoft.requestssimulator.requests;

import br.ufrgs.inf.prosoft.requestssimulator.Session;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author romulo
 */
public class RequestPlan {

  private final String method;
  private final String URL;
  private final String data;
  private String reference;
  private List<RequestPlan> readLinks;
  private List<RequestPlan> writeLinks;
  private Collection<String> storeFields;
  private Collection<String> storeCookies;
  private Collection<RequestPlan> requirements;
  private String headers;
  private String forms;

  private RequestPlan(String method, String URL, String data) {
    this.method = method;
    this.URL = URL;
    this.data = data;
  }

  public static RequestPlan get(String URL) {
    return new RequestPlan("GET", URL, null);
  }

  public static RequestPlan post(String URL, String data) {
    return new RequestPlan("POST", URL, data);
  }

  public static RequestPlan put(String URL, String data) {
    return new RequestPlan("PUT", URL, data);
  }

  public static RequestPlan put(String URL) {
    return put(URL, null);
  }

  public static RequestPlan delete(String URL, String data) {
    return new RequestPlan("DELETE", URL, data);
  }

  public static RequestPlan delete(String URL) {
    return delete(URL, null);
  }

  public String getMethod() {
    return this.method;
  }

  public String getReference() {
    if (this.reference == null) return method + "@" + this.URL;
    return this.reference;
  }

  public RequestPlan setReference(String reference) {
    this.reference = reference;
    return this;
  }

  public RequestPlan addHeader(String header) {
    if (this.headers == null || this.headers.isEmpty()) this.headers = header;
    else this.headers += "; " + header;
    return this;
  }

  public RequestPlan setHeaders(String headers) {
    this.headers = headers;
    return this;
  }

  public RequestPlan addForm(String form) {
    if (this.method.equals("GET")) throw new RuntimeException("Adding form to GET request");
    if (this.forms == null) this.forms = form;
    else this.forms += "; " + form;
    return this;
  }

  public RequestPlan setForms(String forms) {
    if (this.method.equals("GET")) throw new RuntimeException("Adding form to GET request");
    this.forms = forms;
    return this;
  }

  public Stream<RequestPlan> links() {
    if (this.readLinks == null && this.writeLinks == null) return Stream.empty();
    if (this.readLinks != null && this.writeLinks != null) return Stream.concat(this.readLinks.stream(), this.writeLinks.stream());
    if (this.readLinks != null) return this.readLinks.stream();
    return this.writeLinks.stream();
  }

  public RequestPlan addLink(RequestPlan requestPlan) {
    if (requestPlan.getMethod().equals("GET")) {
      if (this.readLinks == null) this.readLinks = new ArrayList<>();
      this.readLinks.add(requestPlan);
    } else {
      if (this.writeLinks == null) this.writeLinks = new ArrayList<>();
      this.writeLinks.add(requestPlan);
    }
    return this;
  }

  public RequestPlan addLinks(Collection<RequestPlan> requestPlans) {
    requestPlans.forEach(this::addLink);
    return this;
  }

  public RequestPlan addRequirement(RequestPlan requestPlan) {
    if (this.requirements == null) this.requirements = new ArrayList<>();
    this.requirements.add(requestPlan);
    return this;
  }

  protected Stream<RequestPlan> requirements() {
    if (this.requirements == null || this.requirements.isEmpty()) return Stream.empty();
    return this.requirements.stream();
  }

  protected Stream<String> storeFields() {
    if (this.storeFields == null || this.storeFields.isEmpty()) return Stream.empty();
    return this.storeFields.stream();
  }

  protected Stream<String> storeCookies() {
    if (this.storeCookies == null || this.storeCookies.isEmpty()) return Stream.empty();
    return this.storeCookies.stream();
  }

  public Request pickNextRequest(Session session) {
    Random random = new Random();
    if ((this.readLinks == null || this.readLinks.isEmpty()) && (this.writeLinks == null || this.writeLinks.isEmpty())) {
      throw new RuntimeException("GET and POST links empty: " + this.URL);
    }
    int probability = random.nextInt(100);
    RequestPlan chosen;
    if (this.readLinks == null || this.readLinks.isEmpty()) probability = 100;
    else if (this.writeLinks == null || this.writeLinks.isEmpty()) probability = 0;
    if (probability < 80) {
      int chosenIndex = random.nextInt(this.readLinks.size());
      chosen = this.readLinks.get(chosenIndex);
    } else {
      int chosenIndex = random.nextInt(this.writeLinks.size());
      chosen = this.writeLinks.get(chosenIndex);
    }
    return chosen.build(session);
  }

  public Request build(Session session) {
    if (this.URL == null || this.URL.isEmpty()) throw new RuntimeException("URL is empty");
    String URL = replaceOptionals(this.URL);
    HashMap<String, Integer> map = new HashMap<>();
    URL = replaceRandoms(URL, map);
    Collection<String> storeFields = storeFields()
      .map(storeField -> replaceRandoms(storeField, map))
      .collect(Collectors.toList());
    String headers = null;
    if (this.headers != null) {
      headers = replaceOptionals(this.headers);
      headers = replaceRandoms(headers, map);
      if (this.headers.contains("multipart")) {
        String forms = replaceOptionals(this.forms);
        return new MultipartRequest(this, session, URL, headers, forms, storeFields);
      }
    }
    if (this.method.equals("DELETE")) return new DeleteRequest(this, session, URL, headers, storeFields);
    if (this.method.equals("GET")) return new GetRequest(this, session, URL, headers, storeFields);
    String data = null;
    if (this.data != null) data = replaceOptionals(this.data);
    if (this.method.equals("PUT")) return new PutRequest(this, session, URL, data, headers, storeFields);
    data = replaceRandoms(data, map);
    return new PostRequest(this, session, URL, data, headers, storeFields);
  }

  private String replaceRandoms(String data) {
    return replaceRandoms(data, new HashMap<>());
  }

  private String replaceRandoms(String data, Map<String, Integer> map) {
    Random random = new Random();
    Pattern pattern = Pattern.compile("\\$\\{(.*?)\\}");
    Matcher randomMatcher = pattern.matcher(data);
    while (randomMatcher.find()) {
      String randomFound = randomMatcher.group();
      String randomTrimmed = randomFound.substring(2, randomFound.length() - 1);
      int randomInt;
      if (map.containsKey(randomFound)) {
        randomInt = map.get(randomFound);
      } else {
        String[] split = randomTrimmed.split("-");
        int from = Integer.valueOf(split[0]);
        int to = Integer.valueOf(split[1]);
        randomInt = random.nextInt(to - from + 1) + from;
        map.put(randomFound, randomInt);
      }
      data = data.replace(randomFound, String.valueOf(randomInt));
    }
    int randomInt;
    if (map.containsKey("$")) {
      randomInt = map.get("$");
    } else {
      randomInt = random.nextInt() & Integer.MAX_VALUE;
      map.put("$", randomInt);
    }
    pattern = Pattern.compile("#\\{(.*?)\\}");
    String[] split = pattern.split(data);
    for (String around : split) {
      String newAround = around.replace("$", String.valueOf(randomInt));
      data = data.replace(around, newAround);
    }
    return data;
  }

  private String replaceOptionals(String data) {
    Pattern pattern = Pattern.compile("<(.*?)>");
    Matcher matcher = pattern.matcher(data);
    Random random = new Random();
    while (matcher.find()) {
      String options = matcher.group();
      String optionsTrimmed = options.substring(1, options.length() - 1);
      String[] split = optionsTrimmed.split("\\|");
      int nextInt = random.nextInt(split.length);
      String choice = split[nextInt];
      data = data.replace(options, choice);
    }
    return data;
  }

  public Request bind(Request request, Session session) {
    if (request instanceof DeleteRequest) {
      DeleteRequest deleteRequest = (DeleteRequest) request;
      return new DeleteRequest(this, session, deleteRequest.getURL(), deleteRequest.getHeaders(), deleteRequest.getStoreFields());
    }
    if (request instanceof GetRequest) {
      GetRequest getRequest = (GetRequest) request;
      return new GetRequest(this, session, getRequest.getURL(), getRequest.getHeaders(), getRequest.getStoreFields());
    }
    if (request instanceof PostRequest) {
      PostRequest postRequest = (PostRequest) request;
      return new PostRequest(this, session, postRequest.getURL(), postRequest.getData(), postRequest.getHeaders(), postRequest.getStoreFields());
    }
    if (request instanceof PutRequest) {
      PutRequest putRequest = (PutRequest) request;
      return new PutRequest(this, session, putRequest.getURL(), putRequest.getData(), putRequest.getHeaders(), putRequest.getStoreFields());
    }
    MultipartRequest multipartRequest = (MultipartRequest) request;
    return new MultipartRequest(this, session, multipartRequest.getURL(), multipartRequest.getHeaders(), multipartRequest.getForms(), multipartRequest.getStoreFields());
  }

  @Override
  public int hashCode() {
    return Objects.hash(this.method, this.URL);
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (!(obj instanceof RequestPlan)) return false;
    RequestPlan other = (RequestPlan) obj;
    return this.method.equals(other.method) && this.URL.equals(other.URL);
  }
}