Example #1
0
    public void handle(HttpRequest request, HttpResponse response, HttpContext arg2)
        throws HttpException, IOException {
      EntityTemplate body =
          new EntityTemplate(
              new ContentProducer() {
                public void writeTo(final OutputStream outstream) throws IOException {
                  OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
                  writer.write("var sounds = [");
                  for (int i = 0; i < raws.length - 1; i++) {
                    writer.write("'" + raws[i].getName() + "',");
                  }
                  writer.write("'" + raws[raws.length - 1].getName() + "'];");
                  writer.write("var screenState = " + (screenState ? "1" : "0") + ";");
                  writer.flush();
                }
              });

      response.setStatusCode(HttpStatus.SC_OK);
      body.setContentType("application/json; charset=UTF-8");
      response.setEntity(body);

      // Bring SpydrdoiActivity to the foreground
      if (!screenState) {
        Intent i = new Intent(context, ServerActivity.class);
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(i);
      }
    }
Example #2
0
    public void handle(HttpRequest request, HttpResponse response, HttpContext arg2)
        throws HttpException, IOException {

      final String uri = URLDecoder.decode(request.getRequestLine().getUri());
      final List<NameValuePair> params = URLEncodedUtils.parse(URI.create(uri), "UTF-8");
      final String[] content = {"Error"};
      int soundID;

      response.setStatusCode(HttpStatus.SC_NOT_FOUND);

      if (params.size() > 0) {
        try {
          for (Iterator<NameValuePair> it = params.iterator(); it.hasNext(); ) {
            NameValuePair param = it.next();
            // Load sound with appropriate name
            if (param.getName().equals("name")) {
              for (int i = 0; i < raws.length; i++) {
                if (raws[i].getName().equals(param.getValue())) {
                  soundID = soundPool.load(context, raws[i].getInt(null), 0);
                  response.setStatusCode(HttpStatus.SC_OK);
                  content[0] = "OK";
                }
              }
            }
          }
        } catch (Exception e) {
          Log.e(TAG, "Error !");
          e.printStackTrace();
        }
      }

      EntityTemplate body =
          new EntityTemplate(
              new ContentProducer() {
                public void writeTo(final OutputStream outstream) throws IOException {
                  OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
                  writer.write(content[0]);
                  writer.flush();
                }
              });
      body.setContentType("text/plain; charset=UTF-8");
      response.setEntity(body);
    }
    public synchronized void handle(HttpRequest request, HttpResponse response, HttpContext context)
        throws HttpException, IOException {
      Socket socket = ((ModifiedHttpContext) context).getSocket();

      // Parse URI and configure the Session accordingly
      final String uri = URLDecoder.decode(request.getRequestLine().getUri());

      final String sessionDescriptor =
          "v=0\r\n"
              + "o=- 15143872582342435176 15143872582342435176 IN IP4 "
              + socket.getLocalAddress().getHostName()
              + "\r\n"
              + "s=Unnamed\r\n"
              + "i=N/A\r\n"
              + "c=IN IP4 "
              + socket.getLocalAddress().getHostAddress()
              + "\r\n"
              + "t=0 0\r\n"
              + "a=tool:spydroid\r\n"
              + "a=recvonly\r\n"
              + "a=type:broadcast\r\n"
              + "a=charset:UTF-8\r\n";

      response.setStatusCode(HttpStatus.SC_OK);
      EntityTemplate body =
          new EntityTemplate(
              new ContentProducer() {
                public void writeTo(final OutputStream outstream) throws IOException {
                  OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
                  writer.write(sessionDescriptor);
                  writer.flush();
                }
              });
      body.setContentType("text/plain; charset=UTF-8");
      response.setEntity(body);
    }
Example #4
0
  @Override
  public void onTrigger(final ProcessContext context, final ProcessSession session) {
    final boolean sendAsFlowFile = context.getProperty(SEND_AS_FLOWFILE).asBoolean();
    final int compressionLevel = context.getProperty(COMPRESSION_LEVEL).asInteger();
    final String userAgent = context.getProperty(USER_AGENT).getValue();

    final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
    requestConfigBuilder.setConnectionRequestTimeout(
        context.getProperty(DATA_TIMEOUT).asTimePeriod(TimeUnit.MILLISECONDS).intValue());
    requestConfigBuilder.setConnectTimeout(
        context.getProperty(CONNECTION_TIMEOUT).asTimePeriod(TimeUnit.MILLISECONDS).intValue());
    requestConfigBuilder.setRedirectsEnabled(false);
    requestConfigBuilder.setSocketTimeout(
        context.getProperty(DATA_TIMEOUT).asTimePeriod(TimeUnit.MILLISECONDS).intValue());
    final RequestConfig requestConfig = requestConfigBuilder.build();

    final StreamThrottler throttler = throttlerRef.get();
    final ProcessorLog logger = getLogger();

    final Double maxBatchBytes = context.getProperty(MAX_BATCH_SIZE).asDataSize(DataUnit.B);
    String lastUrl = null;
    long bytesToSend = 0L;

    final List<FlowFile> toSend = new ArrayList<>();
    DestinationAccepts destinationAccepts = null;
    CloseableHttpClient client = null;
    final String transactionId = UUID.randomUUID().toString();

    final ObjectHolder<String> dnHolder = new ObjectHolder<>("none");
    while (true) {
      FlowFile flowFile = session.get();
      if (flowFile == null) {
        break;
      }

      final String url = context.getProperty(URL).evaluateAttributeExpressions(flowFile).getValue();
      try {
        new java.net.URL(url);
      } catch (final MalformedURLException e) {
        logger.error(
            "After substituting attribute values for {}, URL is {}; this is not a valid URL, so routing to failure",
            new Object[] {flowFile, url});
        flowFile = session.penalize(flowFile);
        session.transfer(flowFile, REL_FAILURE);
        continue;
      }

      // If this FlowFile doesn't have the same url, throw it back on the queue and stop grabbing
      // FlowFiles
      if (lastUrl != null && !lastUrl.equals(url)) {
        session.transfer(flowFile);
        break;
      }

      lastUrl = url;
      toSend.add(flowFile);

      if (client == null || destinationAccepts == null) {
        final Config config = getConfig(url, context);
        final HttpClientConnectionManager conMan = config.getConnectionManager();

        final HttpClientBuilder clientBuilder = HttpClientBuilder.create();
        clientBuilder.setConnectionManager(conMan);
        clientBuilder.setUserAgent(userAgent);
        clientBuilder.addInterceptorFirst(
            new HttpResponseInterceptor() {
              @Override
              public void process(final HttpResponse response, final HttpContext httpContext)
                  throws HttpException, IOException {
                HttpCoreContext coreContext = HttpCoreContext.adapt(httpContext);
                ManagedHttpClientConnection conn =
                    coreContext.getConnection(ManagedHttpClientConnection.class);
                if (!conn.isOpen()) {
                  return;
                }

                SSLSession sslSession = conn.getSSLSession();

                if (sslSession != null) {
                  final X509Certificate[] certChain = sslSession.getPeerCertificateChain();
                  if (certChain == null || certChain.length == 0) {
                    throw new SSLPeerUnverifiedException("No certificates found");
                  }

                  final X509Certificate cert = certChain[0];
                  dnHolder.set(cert.getSubjectDN().getName().trim());
                }
              }
            });

        clientBuilder.disableAutomaticRetries();
        clientBuilder.disableContentCompression();

        final String username = context.getProperty(USERNAME).getValue();
        final String password = context.getProperty(PASSWORD).getValue();
        // set the credentials if appropriate
        if (username != null) {
          final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
          if (password == null) {
            credentialsProvider.setCredentials(
                AuthScope.ANY, new UsernamePasswordCredentials(username));
          } else {
            credentialsProvider.setCredentials(
                AuthScope.ANY, new UsernamePasswordCredentials(username, password));
          }
          clientBuilder.setDefaultCredentialsProvider(credentialsProvider);
        }
        client = clientBuilder.build();

        // determine whether or not destination accepts flowfile/gzip
        destinationAccepts = config.getDestinationAccepts();
        if (destinationAccepts == null) {
          try {
            if (sendAsFlowFile) {
              destinationAccepts =
                  getDestinationAcceptance(client, url, getLogger(), transactionId);
            } else {
              destinationAccepts = new DestinationAccepts(false, false, false, false, null);
            }

            config.setDestinationAccepts(destinationAccepts);
          } catch (IOException e) {
            flowFile = session.penalize(flowFile);
            session.transfer(flowFile, REL_FAILURE);
            logger.error(
                "Unable to communicate with destination {} to determine whether or not it can accept "
                    + "flowfiles/gzip; routing {} to failure due to {}",
                new Object[] {url, flowFile, e});
            context.yield();
            return;
          }
        }
      }

      // if we are not sending as flowfile, or if the destination doesn't accept V3 or V2
      // (streaming) format,
      // then only use a single FlowFile
      if (!sendAsFlowFile
          || (!destinationAccepts.isFlowFileV3Accepted()
              && !destinationAccepts.isFlowFileV2Accepted())) {
        break;
      }

      bytesToSend += flowFile.getSize();
      if (bytesToSend > maxBatchBytes.longValue()) {
        break;
      }
    }

    if (toSend.isEmpty()) {
      return;
    }

    final String url = lastUrl;
    final HttpPost post = new HttpPost(url);
    final List<FlowFile> flowFileList = toSend;
    final DestinationAccepts accepts = destinationAccepts;
    final boolean isDestinationLegacyNiFi = accepts.getProtocolVersion() == null;

    final EntityTemplate entity =
        new EntityTemplate(
            new ContentProducer() {
              @Override
              public void writeTo(final OutputStream rawOut) throws IOException {
                final OutputStream throttled =
                    (throttler == null) ? rawOut : throttler.newThrottledOutputStream(rawOut);
                OutputStream wrappedOut = new BufferedOutputStream(throttled);
                if (compressionLevel > 0 && accepts.isGzipAccepted()) {
                  wrappedOut = new GZIPOutputStream(wrappedOut, compressionLevel);
                }

                try (final OutputStream out = wrappedOut) {
                  for (final FlowFile flowFile : flowFileList) {
                    session.read(
                        flowFile,
                        new InputStreamCallback() {
                          @Override
                          public void process(final InputStream rawIn) throws IOException {
                            try (final InputStream in = new BufferedInputStream(rawIn)) {

                              FlowFilePackager packager = null;
                              if (!sendAsFlowFile) {
                                packager = null;
                              } else if (accepts.isFlowFileV3Accepted()) {
                                packager = new FlowFilePackagerV3();
                              } else if (accepts.isFlowFileV2Accepted()) {
                                packager = new FlowFilePackagerV2();
                              } else if (accepts.isFlowFileV1Accepted()) {
                                packager = new FlowFilePackagerV1();
                              }

                              // if none of the above conditions is met, we should never get here,
                              // because
                              // we will have already verified that at least 1 of the FlowFile
                              // packaging
                              // formats is acceptable if sending as FlowFile.
                              if (packager == null) {
                                StreamUtils.copy(in, out);
                              } else {
                                final Map<String, String> flowFileAttributes;
                                if (isDestinationLegacyNiFi) {
                                  // Old versions of NiFi expect nf.file.name and nf.file.path to
                                  // indicate filename & path;
                                  // in order to maintain backward compatibility, we copy the
                                  // filename & path to those attribute keys.
                                  flowFileAttributes = new HashMap<>(flowFile.getAttributes());
                                  flowFileAttributes.put(
                                      "nf.file.name",
                                      flowFile.getAttribute(CoreAttributes.FILENAME.key()));
                                  flowFileAttributes.put(
                                      "nf.file.path",
                                      flowFile.getAttribute(CoreAttributes.PATH.key()));
                                } else {
                                  flowFileAttributes = flowFile.getAttributes();
                                }

                                packager.packageFlowFile(
                                    in, out, flowFileAttributes, flowFile.getSize());
                              }
                            }
                          }
                        });
                  }

                  out.flush();
                }
              }
            });

    entity.setChunked(context.getProperty(CHUNKED_ENCODING).asBoolean());
    post.setEntity(entity);
    post.setConfig(requestConfig);

    final String contentType;
    if (sendAsFlowFile) {
      if (accepts.isFlowFileV3Accepted()) {
        contentType = APPLICATION_FLOW_FILE_V3;
      } else if (accepts.isFlowFileV2Accepted()) {
        contentType = APPLICATION_FLOW_FILE_V2;
      } else if (accepts.isFlowFileV1Accepted()) {
        contentType = APPLICATION_FLOW_FILE_V1;
      } else {
        logger.error(
            "Cannot send data to {} because the destination does not accept FlowFiles and this processor is "
                + "configured to deliver FlowFiles; rolling back session",
            new Object[] {url});
        session.rollback();
        context.yield();
        return;
      }
    } else {
      final String attributeValue = toSend.get(0).getAttribute(CoreAttributes.MIME_TYPE.key());
      contentType = (attributeValue == null) ? DEFAULT_CONTENT_TYPE : attributeValue;
    }

    final String attributeHeaderRegex = context.getProperty(ATTRIBUTES_AS_HEADERS_REGEX).getValue();
    if (attributeHeaderRegex != null && !sendAsFlowFile && flowFileList.size() == 1) {
      final Pattern pattern = Pattern.compile(attributeHeaderRegex);

      final Map<String, String> attributes = flowFileList.get(0).getAttributes();
      for (final Map.Entry<String, String> entry : attributes.entrySet()) {
        final String key = entry.getKey();
        if (pattern.matcher(key).matches()) {
          post.setHeader(entry.getKey(), entry.getValue());
        }
      }
    }

    post.setHeader(CONTENT_TYPE, contentType);
    post.setHeader(FLOWFILE_CONFIRMATION_HEADER, "true");
    post.setHeader(PROTOCOL_VERSION_HEADER, PROTOCOL_VERSION);
    post.setHeader(TRANSACTION_ID_HEADER, transactionId);
    if (compressionLevel > 0 && accepts.isGzipAccepted()) {
      post.setHeader(GZIPPED_HEADER, "true");
    }

    // Do the actual POST
    final String flowFileDescription =
        toSend.size() <= 10 ? toSend.toString() : toSend.size() + " FlowFiles";

    final String uploadDataRate;
    final long uploadMillis;
    CloseableHttpResponse response = null;
    try {
      final StopWatch stopWatch = new StopWatch(true);
      response = client.execute(post);

      // consume input stream entirely, ignoring its contents. If we
      // don't do this, the Connection will not be returned to the pool
      EntityUtils.consume(response.getEntity());
      stopWatch.stop();
      uploadDataRate = stopWatch.calculateDataRate(bytesToSend);
      uploadMillis = stopWatch.getDuration(TimeUnit.MILLISECONDS);
    } catch (final IOException e) {
      logger.error(
          "Failed to Post {} due to {}; transferring to failure",
          new Object[] {flowFileDescription, e});
      context.yield();
      for (FlowFile flowFile : toSend) {
        flowFile = session.penalize(flowFile);
        session.transfer(flowFile, REL_FAILURE);
      }
      return;
    } finally {
      if (response != null) {
        try {
          response.close();
        } catch (IOException e) {
          getLogger().warn("Failed to close HTTP Response due to {}", new Object[] {e});
        }
      }
    }

    // If we get a 'SEE OTHER' status code and an HTTP header that indicates that the intent
    // of the Location URI is a flowfile hold, we will store this holdUri. This prevents us
    // from posting to some other webservice and then attempting to delete some resource to which
    // we are redirected
    final int responseCode = response.getStatusLine().getStatusCode();
    final String responseReason = response.getStatusLine().getReasonPhrase();
    String holdUri = null;
    if (responseCode == HttpServletResponse.SC_SEE_OTHER) {
      final Header locationUriHeader = response.getFirstHeader(LOCATION_URI_INTENT_NAME);
      if (locationUriHeader != null) {
        if (LOCATION_URI_INTENT_VALUE.equals(locationUriHeader.getValue())) {
          final Header holdUriHeader = response.getFirstHeader(LOCATION_HEADER_NAME);
          if (holdUriHeader != null) {
            holdUri = holdUriHeader.getValue();
          }
        }
      }

      if (holdUri == null) {
        for (FlowFile flowFile : toSend) {
          flowFile = session.penalize(flowFile);
          logger.error(
              "Failed to Post {} to {}: sent content and received status code {}:{} but no Hold URI",
              new Object[] {flowFile, url, responseCode, responseReason});
          session.transfer(flowFile, REL_FAILURE);
        }
        return;
      }
    }

    if (holdUri == null) {
      if (responseCode == HttpServletResponse.SC_SERVICE_UNAVAILABLE) {
        for (FlowFile flowFile : toSend) {
          flowFile = session.penalize(flowFile);
          logger.error(
              "Failed to Post {} to {}: response code was {}:{}; will yield processing, "
                  + "since the destination is temporarily unavailable",
              new Object[] {flowFile, url, responseCode, responseReason});
          session.transfer(flowFile, REL_FAILURE);
        }
        context.yield();
        return;
      }

      if (responseCode >= 300) {
        for (FlowFile flowFile : toSend) {
          flowFile = session.penalize(flowFile);
          logger.error(
              "Failed to Post {} to {}: response code was {}:{}",
              new Object[] {flowFile, url, responseCode, responseReason});
          session.transfer(flowFile, REL_FAILURE);
        }
        return;
      }

      logger.info(
          "Successfully Posted {} to {} in {} at a rate of {}",
          new Object[] {
            flowFileDescription,
            url,
            FormatUtils.formatMinutesSeconds(uploadMillis, TimeUnit.MILLISECONDS),
            uploadDataRate
          });

      for (final FlowFile flowFile : toSend) {
        session
            .getProvenanceReporter()
            .send(flowFile, url, "Remote DN=" + dnHolder.get(), uploadMillis, true);
        session.transfer(flowFile, REL_SUCCESS);
      }
      return;
    }

    //
    // the response indicated a Hold URI; delete the Hold.
    //
    // determine the full URI of the Flow File's Hold; Unfortunately, the responses that are
    // returned have
    // changed over the past, so we have to take into account a few different possibilities.
    String fullHoldUri = holdUri;
    if (holdUri.startsWith("/contentListener")) {
      // If the Hold URI that we get starts with /contentListener, it may not really be
      // /contentListener,
      // as this really indicates that it should be whatever we posted to -- if posting directly to
      // the
      // ListenHTTP component, it will be /contentListener, but if posting to a proxy/load balancer,
      // we may
      // be posting to some other URL.
      fullHoldUri = url + holdUri.substring(16);
    } else if (holdUri.startsWith("/")) {
      // URL indicates the full path but not hostname or port; use the same hostname & port that we
      // posted
      // to but use the full path indicated by the response.
      int firstSlash = url.indexOf("/", 8);
      if (firstSlash < 0) {
        firstSlash = url.length();
      }
      final String beforeSlash = url.substring(0, firstSlash);
      fullHoldUri = beforeSlash + holdUri;
    } else if (!holdUri.startsWith("http")) {
      // Absolute URL
      fullHoldUri = url + (url.endsWith("/") ? "" : "/") + holdUri;
    }

    final HttpDelete delete = new HttpDelete(fullHoldUri);
    delete.setHeader(TRANSACTION_ID_HEADER, transactionId);

    while (true) {
      try {
        final HttpResponse holdResponse = client.execute(delete);
        EntityUtils.consume(holdResponse.getEntity());
        final int holdStatusCode = holdResponse.getStatusLine().getStatusCode();
        final String holdReason = holdResponse.getStatusLine().getReasonPhrase();
        if (holdStatusCode >= 300) {
          logger.error(
              "Failed to delete Hold that destination placed on {}: got response code {}:{}; routing to failure",
              new Object[] {flowFileDescription, holdStatusCode, holdReason});

          for (FlowFile flowFile : toSend) {
            flowFile = session.penalize(flowFile);
            session.transfer(flowFile, REL_FAILURE);
          }
          return;
        }

        logger.info(
            "Successfully Posted {} to {} in {} milliseconds at a rate of {}",
            new Object[] {flowFileDescription, url, uploadMillis, uploadDataRate});

        for (FlowFile flowFile : toSend) {
          session.getProvenanceReporter().send(flowFile, url);
          session.transfer(flowFile, REL_SUCCESS);
        }
        return;
      } catch (final IOException e) {
        logger.warn(
            "Failed to delete Hold that destination placed on {} due to {}",
            new Object[] {flowFileDescription, e});
      }

      if (!isScheduled()) {
        context.yield();
        logger.warn(
            "Failed to delete Hold that destination placed on {}; Processor has been stopped so routing FlowFile(s) to failure",
            new Object[] {flowFileDescription});
        for (FlowFile flowFile : toSend) {
          flowFile = session.penalize(flowFile);
          session.transfer(flowFile, REL_FAILURE);
        }
        return;
      }
    }
  }
Example #5
0
    public void handle(HttpRequest request, HttpResponse response, HttpContext httpContext)
        throws HttpException, IOException {

      SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
      final String uri = URLDecoder.decode(request.getRequestLine().getUri());
      final List<NameValuePair> params = URLEncodedUtils.parse(URI.create(uri), "UTF-8");
      String result = "Error";

      if (params.size() > 0) {
        try {

          // Set the configuration
          if (params.get(0).getName().equals("set")) {
            Editor editor = settings.edit();
            editor.putBoolean("stream_audio", false);
            editor.putBoolean("stream_video", false);
            for (Iterator<NameValuePair> it = params.iterator(); it.hasNext(); ) {
              NameValuePair param = it.next();
              if (param.getName().equals("h263") || param.getName().equals("h264")) {
                editor.putBoolean("stream_video", true);
                Session.defaultVideoQuality = VideoQuality.parseQuality(param.getValue());
                editor.putInt("video_resX", Session.defaultVideoQuality.resX);
                editor.putInt("video_resY", Session.defaultVideoQuality.resY);
                editor.putString(
                    "video_framerate", String.valueOf(Session.defaultVideoQuality.frameRate));
                editor.putString(
                    "video_bitrate", String.valueOf(Session.defaultVideoQuality.bitRate / 1000));
                editor.putString("video_encoder", param.getName().equals("h263") ? "2" : "1");
              }
              if (param.getName().equals("amr") || param.getName().equals("aac")) {
                editor.putBoolean("stream_audio", true);
                Session.defaultVideoQuality = VideoQuality.parseQuality(param.getValue());
                editor.putString("audio_encoder", param.getName().equals("amr") ? "3" : "5");
              }
            }
            editor.commit();
            result = "[]";
          }

          // Send the current streaming configuration to the client
          else if (params.get(0).getName().equals("get")) {
            result =
                "{\""
                    + HX_DETECt_TAG
                    + "\": true,"
                    + "\"streamAudio\":"
                    + settings.getBoolean("stream_audio", false)
                    + ","
                    + "\"audioEncoder\":\""
                    + (Integer.parseInt(settings.getString("audio_encoder", "3")) == 3
                        ? "AMR-NB"
                        : "AAC")
                    + "\","
                    + "\"streamVideo\":"
                    + settings.getBoolean("stream_video", true)
                    + ","
                    + "\"videoEncoder\":\""
                    + (Integer.parseInt(settings.getString("video_encoder", "2")) == 2
                        ? "H.263"
                        : "H.264")
                    + "\","
                    + "\"videoResolution\":\""
                    + settings.getInt("video_resX", Session.defaultVideoQuality.resX)
                    + "x"
                    + settings.getInt("video_resY", Session.defaultVideoQuality.resY)
                    + "\","
                    + "\"videoFramerate\":\""
                    + settings.getString(
                        "video_framerate", String.valueOf(Session.defaultVideoQuality.frameRate))
                    + " fps\","
                    + "\"videoBitrate\":\""
                    + settings.getString(
                        "video_bitrate", String.valueOf(Session.defaultVideoQuality.bitRate / 1000))
                    + " kbps\"}";
          }
        } catch (Exception e) {
          Log.e(TAG, "Error !");
          e.printStackTrace();
        }
      }

      final String finalResult = result;
      EntityTemplate body =
          new EntityTemplate(
              new ContentProducer() {
                public void writeTo(final OutputStream outstream) throws IOException {
                  OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
                  writer.write(finalResult);
                  writer.flush();
                }
              });

      response.setStatusCode(HttpStatus.SC_OK);
      body.setContentType("application/json; charset=UTF-8");
      response.setEntity(body);
    }
  public void postJsonToPipeline(String endpoint, List docs, int requestId) throws Exception {

    FusionSession fusionSession = null;

    long currTime = System.nanoTime();
    synchronized (this) {
      fusionSession = sessions.get(endpoint);

      // ensure last request within the session timeout period, else reset the session
      if (fusionSession == null
          || (currTime - fusionSession.sessionEstablishedAt) > maxNanosOfInactivity) {
        log.info(
            "Fusion session is likely expired (or soon will be) for endpoint "
                + endpoint
                + ", "
                + "pre-emptively re-setting this session before processing request "
                + requestId);
        fusionSession = resetSession(endpoint);
        if (fusionSession == null)
          throw new IllegalStateException(
              "Failed to re-connect to "
                  + endpoint
                  + " after session loss when processing request "
                  + requestId);
      }
    }

    HttpEntity entity = null;
    try {
      HttpPost postRequest = new HttpPost(endpoint);

      // stream the json directly to the HTTP output
      EntityTemplate et = new EntityTemplate(new JacksonContentProducer(jsonObjectMapper, docs));
      et.setContentType("application/json; charset=utf-8");
      et.setContentEncoding("gzip"); // StandardCharsets.UTF_8.name());
      postRequest.setEntity(et); // new BufferedHttpEntity(et));

      HttpClientContext context = HttpClientContext.create();
      context.setCookieStore(cookieStore);

      HttpResponse response = httpClient.execute(postRequest, context);
      entity = response.getEntity();
      int statusCode = response.getStatusLine().getStatusCode();
      if (statusCode == 401) {
        // unauth'd - session probably expired? retry to establish
        log.warn(
            "Unauthorized error (401) when trying to send request "
                + requestId
                + " to Fusion at "
                + endpoint
                + ", will re-try to establish session");

        // re-establish the session and re-try the request
        try {
          EntityUtils.consume(entity);
        } catch (Exception ignore) {
          log.warn("Failed to consume entity due to: " + ignore);
        } finally {
          entity = null;
        }

        synchronized (this) {
          fusionSession = resetSession(endpoint);
          if (fusionSession == null)
            throw new IllegalStateException(
                "After re-establishing session when processing request "
                    + requestId
                    + ", endpoint "
                    + endpoint
                    + " is no longer active! Try another endpoint.");
        }

        log.info(
            "Going to re-try request "
                + requestId
                + " after session re-established with "
                + endpoint);
        response = httpClient.execute(postRequest, context);
        entity = response.getEntity();
        statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200 || statusCode == 204) {
          log.info(
              "Re-try request " + requestId + " after session timeout succeeded for: " + endpoint);
        } else {
          raiseFusionServerException(endpoint, entity, statusCode, response, requestId);
        }
      } else if (statusCode != 200 && statusCode != 204) {
        raiseFusionServerException(endpoint, entity, statusCode, response, requestId);
      } else {
        // OK!
      }
    } finally {

      if (entity != null) {
        try {
          EntityUtils.consume(entity);
        } catch (Exception ignore) {
          log.warn("Failed to consume entity due to: " + ignore);
        } finally {
          entity = null;
        }
      }
    }
  }
    public void handle(HttpRequest request, HttpResponse response, HttpContext arg2)
        throws HttpException, IOException {

      final String uri = URLDecoder.decode(request.getRequestLine().getUri());
      final List<NameValuePair> params = URLEncodedUtils.parse(URI.create(uri), "UTF-8");
      final String[] content = {"Error"};
      int soundID;

      response.setStatusCode(HttpStatus.SC_NOT_FOUND);

      if (params.size() > 0) {
        try {
          for (Iterator<NameValuePair> it = params.iterator(); it.hasNext(); ) {
            NameValuePair param = it.next();
            // Load sound with appropriate name
            if (param.getName().equals("name")) {
              for (int i = 0; i < raws.length; i++) {
                if (raws[i].getName().equals(param.getValue())) {
                  soundID = soundPool.load(context, raws[i].getInt(null), 0);
                  response.setStatusCode(HttpStatus.SC_OK);
                  content[0] = "OK";
                }
              }
              if (param.getValue().equals("forward")) {
                handler.obtainMessage(MOVE_FORWARD).sendToTarget();
                // soundID = soundPool.load(context, raws[0].getInt(null), 0);
                response.setStatusCode(HttpStatus.SC_OK);
                content[0] = "OK";
              }
              if (param.getValue().equals("backward")) {
                handler.obtainMessage(MOVE_BACKWARD).sendToTarget();
                // soundID = soundPool.load(context, raws[0].getInt(null), 0);
                response.setStatusCode(HttpStatus.SC_OK);
                content[0] = "OK";
              }
              if (param.getValue().equals("left")) {
                handler.obtainMessage(MOVE_LEFT).sendToTarget();
                // soundID = soundPool.load(context, raws[0].getInt(null), 0);
                response.setStatusCode(HttpStatus.SC_OK);
                content[0] = "OK";
              }
              if (param.getValue().equals("right")) {
                handler.obtainMessage(MOVE_RIGHT).sendToTarget();
                // soundID = soundPool.load(context, raws[0].getInt(null), 0);
                response.setStatusCode(HttpStatus.SC_OK);
                content[0] = "OK";
              }
              if (param.getValue().equals("stop")) {
                handler.obtainMessage(STOP).sendToTarget();
                // soundID = soundPool.load(context, raws[0].getInt(null), 0);
                response.setStatusCode(HttpStatus.SC_OK);
                content[0] = "OK";
              }
              if (param.getValue().equals("obstacleOn")) {
                handler.obtainMessage(ENABLE_OBSTACLE_AVOIDANCE).sendToTarget();
                // soundID = soundPool.load(context, raws[0].getInt(null), 0);
                response.setStatusCode(HttpStatus.SC_OK);
                content[0] = "OK";
              }
              if (param.getValue().equals("obstacleOff")) {
                handler.obtainMessage(DISABLE_OBSTACLE_AVOIDANCE).sendToTarget();
                // soundID = soundPool.load(context, raws[1].getInt(null), 0);
                response.setStatusCode(HttpStatus.SC_OK);
                content[0] = "OK";
              }
              if (param.getValue().equals("cliffOn")) {
                handler.obtainMessage(ENABLE_CLIFF_AVOIDANCE).sendToTarget();
                // soundID = soundPool.load(context, raws[2].getInt(null), 0);
                response.setStatusCode(HttpStatus.SC_OK);
                content[0] = "OK";
              }
              if (param.getValue().equals("cliffOff")) {
                handler.obtainMessage(DISABLE_CLIFF_AVOIDANCE).sendToTarget();
                // soundID = soundPool.load(context, raws[3].getInt(null), 0);
                response.setStatusCode(HttpStatus.SC_OK);
                content[0] = "OK";
              }
            }
          }
        } catch (Exception e) {
          Log.e(TAG, "Error !");
          e.printStackTrace();
        }
      }

      EntityTemplate body =
          new EntityTemplate(
              new ContentProducer() {
                public void writeTo(final OutputStream outstream) throws IOException {
                  OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
                  writer.write(content[0]);
                  writer.flush();
                }
              });
      body.setContentType("text/plain; charset=UTF-8");
      response.setEntity(body);
    }