⚗️ Lightweight HTTP extensions for Java 11

Overview

Methanol

A lightweight library that complements java.net.http for a better HTTP experience.

CI status Coverage Status Maven Central

Overview

Methanol provides useful lightweight HTTP extensions aimed at making it much easier to work with java.net.http. Applications using Java's non-blocking HTTP client shall find it more robust and easier to use with Methanol.

Features

  • Automatic response decompression.
  • Special BodyPublisher implementations for form submission.
  • Extensible object conversion mechanism, with support for JSON, XML and Protocol Buffers out of the box.
  • Enhanced HttpClient with interceptors, request decoration and async Publisher<HttpResponse<T>> dispatches.
  • Progress tracking for upload and download operations.
  • Additional BodyPublisher, BodySubscriber and BodyHandler implementations.

Installation

Gradle

dependencies {
  implementation 'com.github.mizosoft.methanol:methanol:1.4.1'
}

Maven

<dependencies>
  <dependency>
    <groupId>com.github.mizosoft.methanol</groupId>
    <artifactId>methanol</artifactId>
    <version>1.4.1</version>
  </dependency>
</dependencies>

Documentation

Examples

Response decompression

The HTTP client has no native decompression support. Methanol ameliorates this in a flexible and reactive-friendly way so that you don't have to use blocking streams like GZIPInputStream.

final HttpClient client = HttpClient.newHttpClient();

<T> T get(String url, BodyHandler<T> handler) throws IOException, InterruptedException {
  MutableRequest request = MutableRequest.GET(url)
      .header("Accept-Encoding", "gzip");
  HttpResponse<T> response = client.send(request, MoreBodyHandlers.decoding(handler));
  int statusCode = response.statusCode();
  if (statusCode < 200 || statusCode > 299) {
    throw new IOException("failed response: " + statusCode);
  }

  return response.body();
}

Object conversion

Methanol provides a flexible mechanism for dynamically converting objects to or from request or response bodies respectively. This example interacts with GitHub's JSON API. It is assumed you have methanol-gson or methanol-jackson installed.

final Methanol client = Methanol.newBuilder()
    .baseUri("https://api.github.com")
    .defaultHeader("Accept", "application/vnd.github.v3+json")
    .build();

GitHubUser getUser(String name) throws IOException, InterruptedException {
  MutableRequest request = MutableRequest.GET("/users/" + name);
  HttpResponse<GitHubUser> response =
      client.send(request, MoreBodyHandlers.ofObject(GitHubUser.class));

  return response.body();
}

// For complex types, use a TypeRef
List<GitHubUser> getUserFollowers(String userName) throws IOException, InterruptedException {
  MutableRequest request = MutableRequest.GET("/users/" + userName + "/followers");
  HttpResponse<List<GitHubUser>> response =
      client.send(request, MoreBodyHandlers.ofObject(new TypeRef<List<GitHubUser>>() {}));

  return response.body();
}

String renderMarkdown(RenderRequest renderRequest) throws IOException, InterruptedException {
  BodyPublisher requestBody = MoreBodyPublishers.ofObject(renderRequest, MediaType.APPLICATION_JSON);
  // No need to set Content-Type header!
  MutableRequest request = MutableRequest.POST("/markdown", requestBody)
      .header("Accept", "text/html");
  HttpResponse<String> response = client.send(request, BodyHandlers.ofString());

  return response.body();
}

static class GitHubUser {
  public String login;
  public long id;
  public String bio;
  // other fields omitted
}

static class RenderRequest {
  public String text, mode, context;
}

Form bodies

You can use FormBodyPublisher for submitting URL-encoded forms. In this example, an article is downloaded from Wikipedia using a provided search query.

final Methanol client = Methanol.newBuilder()
    .baseUri("https://en.wikipedia.org")
    .followRedirects(HttpClient.Redirect.NORMAL)
    .build();

Path downloadArticle(String title) throws IOException, InterruptedException {
  FormBodyPublisher searchQuery = FormBodyPublisher.newBuilder()
      .query("search", title)
      .build();
  MutableRequest request = MutableRequest.POST("/wiki/Main_Page", searchQuery);
  HttpResponse<Path> response =
      client.send(request, BodyHandlers.ofFile(Path.of(title + ".html")));

  return response.body();
}

Multipart bodies

The library also provides flexible support for multipart. In this example, a multipart body is used to upload an image to imgur.

final Methanol client = Methanol.newBuilder()
    .baseUri("https://api.imgur.com/3/")
    .defaultHeader("Authorization", "Client-ID " + System.getenv("IMGUR_CLIENT_ID")) // substitute with your client ID
    .build();

URI uploadToImgur(String title, Path image) throws IOException, InterruptedException {
  MultipartBodyPublisher imageUpload = MultipartBodyPublisher.newBuilder()
      .textPart("title", title)
      .filePart("image", image)
      .build();
  MutableRequest request = MutableRequest.POST("upload", imageUpload);
  HttpResponse<Reader> response = client.send(request, MoreBodyHandlers.ofReader());

  try (Reader reader = response.body()) {
    String link = com.google.gson.JsonParser.parseReader(reader)
        .getAsJsonObject()
        .getAsJsonObject("data")
        .get("link")
        .getAsString();

    return URI.create(link);
  }
}

Reactive request dispatches

For a truly reactive experience, one might want to dispatch async requests as Publisher<HttpResponse<T>> sources. Methanol client complements sendAsync with exchange for such a task. This example assumes you have methanol-jackson-flux installed.

final Methanol client = Methanol.newBuilder()
    .baseUri("https://api.github.com")
    .defaultHeader("Accept", "application/vnd.github.v3+json")
    .build();

Flux<GitHubUser> getContributors(String repo) {
  MutableRequest request = MutableRequest.GET("/repos/" + repo + "/contributors");
  Publisher<HttpResponse<Flux<GitHubUser>>> publisher =
      client.exchange(request, MoreBodyHandlers.ofObject(new TypeRef<Flux<GitHubUser>>() {}));
  
  return JdkFlowAdapter.flowPublisherToFlux(publisher).flatMap(HttpResponse::body);
}

Push promises

This also works well with push-promise enabled servers. Here, the publisher streams a non-ordered sequence including the main response along with other resources pushed by the server.

Methanol client = Methanol.create(); // default Version is HTTP_2
MutableRequest request = MutableRequest.GET("https://http2.golang.org/serverpush");
Publisher<HttpResponse<Path>> publisher =
    client.exchange(
        request,
        BodyHandlers.ofFile(Path.of("index.html")),
        promise -> BodyHandlers.ofFile(Path.of(promise.uri().getPath()).getFileName()));
JdkFlowAdapter.flowPublisherToFlux(publisher)
    .filter(res -> res.statusCode() == 200)
    .map(HttpResponse::body)
    .subscribe(System.out::println);

Tracking progress

A responsive application needs a method to provide progression feedback for long-running tasks. ProgressTracker comes in handy in such case. This example logs progress events of a large file download.

final HttpClient client = HttpClient.newHttpClient();
final ProgressTracker tracker =
    ProgressTracker.newBuilder()
        .timePassedThreshold(Duration.ofSeconds(1))
        .build();

Path download() throws IOException, InterruptedException {
  MutableRequest request = MutableRequest.GET("https://norvig.com/big.txt");
  HttpResponse<Path> response =
      client.send(request, tracker.tracking(BodyHandlers.ofFile(Path.of("big.txt")), this::logProgress));

  return response.body();
}

void logProgress(Progress progress) {
  var record = "Downloaded: " + progress.totalBytesTransferred() + " bytes";

  // log percentage if possible
  if (progress.determinate()) {
    record += " (" + round(100.d * progress.value()) + "%)";
  }

  // log download speed
  long millis = progress.timePassed().toMillis();
  if (millis > 0L) {
    float bytesPerSecond = (1.f * progress.bytesTransferred() / millis);
    record += " (" + round(bytesPerSecond * (1000.f / 1024)) + " KB/s)";
  }

  System.out.println(record);
}

static float round(double value) {
  return Math.round(100.f * value) / 100.f;
}

Contributing

See CONTRIBUTING

License

MIT

Comments
  • External cache stores

    External cache stores

    Hi,

    We're looking at the feature set this library provides and we noticed that currently only in memory and on disk caching stores are supported. Are there any plans to support external stores, e.g. redis, to name one? Our use case is we typically have services running on multiple containers behind an ELB and with local cache stores the hit/miss ratio is not optimal. For services that run on other stacks that support external caching we usually have a redis cluster sitting on the side that serves as a central, shared place for all the containers to read/write cache entries.

    Thanks!

    opened by jfeltesse-mdsol 10
  • Service Loader does not work in Spring Boot

    Service Loader does not work in Spring Boot

    Hi, definition Decoder and Encoder are not defined in the system.

    private List<S> loadProviders() {
        List<S> providers = new ArrayList<>();
        ServiceLoader.load(service, Thread.currentThread().getContextClassLoader()).stream()
            .forEach(p -> addProviderLenient(p, providers));
        return Collections.unmodifiableList(providers);
      }
    

    change to "Thread.currentThread().getContextClassLoader()" solves the problem

    P.S. - https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html#executable-jar.restrictions:

    System classLoader: Launched applications should use Thread.getContextClassLoader() when loading classes (most libraries and frameworks do so by default). Trying to load nested jar classes with ClassLoader.getSystemClassLoader() fails. java.util.Logging always uses the system classloader. For this reason, you should consider a different logging implementation.

    locally tested, the problem disappeared

    opened by PRIESt512 9
  • readTimeout and connectTimeout have no affect if server does return HTTP headers

    readTimeout and connectTimeout have no affect if server does return HTTP headers

    When server establishes a socket connection, but never sends anything back HttpCleint gets stuck

    	at [email protected]/jdk.internal.misc.Unsafe.park(Native Method)
    	at [email protected]/java.util.concurrent.locks.LockSupport.park(LockSupport.java:194)
    	at [email protected]/java.util.concurrent.CompletableFuture$Signaller.block(CompletableFuture.java:1796)
    	at [email protected]/java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3128)
    	at [email protected]/java.util.concurrent.CompletableFuture.waitingGet(CompletableFuture.java:1823)
    	at [email protected]/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1998)
    	at platform/[email protected]/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:541)
    	at platform/[email protected]/jdk.internal.net.http.HttpClientFacade.send(HttpClientFacade.java:119)
    	at app//com.github.mizosoft.methanol.Methanol$InterceptorChain.forward(Methanol.java:775)
    	at app//com.github.mizosoft.methanol.Methanol$ReadTimeoutInterceptor.intercept(Methanol.java:944)
    	at app//com.github.mizosoft.methanol.Methanol$InterceptorChain.forward(Methanol.java:780)
    	at app//com.github.mizosoft.methanol.Methanol$AutoDecompressingInterceptor.intercept(Methanol.java:884)
    	at app//com.github.mizosoft.methanol.Methanol$InterceptorChain.forward(Methanol.java:780)
    	at app//com.github.mizosoft.methanol.Methanol$RequestRewritingInterceptor.intercept(Methanol.java:828)
    	at app//com.github.mizosoft.methanol.Methanol$InterceptorChain.forward(Methanol.java:780)
    	at app//com.github.mizosoft.methanol.Methanol.send(Methanol.java:300)
    

    I don't think it is a Methanol issue itself, but maybe at can be fixed with a little help from Methanol ?

    opened by rymsha 6
  • Multipart request hangs if server throws an error (ending it prematurely)

    Multipart request hangs if server throws an error (ending it prematurely)

    Hi, I have been facing this issue lately (which was really hard to debug), when a .join() call to a CompletableFuture of a multipart upload request (called in a background thread) would hang until the request timeout was exceeded.

    After some extensive testing, I realised that this happens when the server throws an error at some point (either at the very beginning or during the files upload) and sends a 500 response. When this happens, the CompletableFuture does not complete (until the timeout is hit).

    I am fairly certain that this is not a bug of Methanol, since it also happens with a custom MultiPartBodyPublisher I used before I switched to Methanol's implementation. Also, the server side seems to handle the response correctly, since testing the same request with Postman immediately produces the desired result (the request is ended with an error response).

    Nevertheless, I am wondering if you have any idea of how to handle this properly.

    opened by nemphys 6
  • headersTimeout and connectTimeout together create a resource leak

    headersTimeout and connectTimeout together create a resource leak

    When connectTimeout headersTimeout are used together

            final HttpRequest request = HttpRequest.newBuilder().uri( URI.create( "https://example.com/" ) ).build();
            final var client = Methanol.newBuilder()
                .connectTimeout( Duration.of( 20000, ChronoUnit.MILLIS ) )
                .headersTimeout( Duration.of( 25000, ChronoUnit.MILLIS ) )
                .build();
            client.send( request, HttpResponse.BodyHandlers.discarding() );
    
            for ( int i = 0; i < 10; i++ ) {
                System.gc();
                Thread.sleep( 10000 );
            }
    
    

    HttpClient is never garbage collected: image

    I guess a simple workaround is not to use connectTimeout when headersTimeout is set. But I still curious if it can be improved.

    Tested on JDK 11 latest.

    opened by rymsha 4
  • Headers behavior in ResponseBuilder

    Headers behavior in ResponseBuilder

    I'm moving some code over from OkHttp and had a gotcha moment today that ate up a lot of my day. Out client was sending multiple copys of request headers and we couldn't figire it out.

    In the ResponseBuilder class "headers" is used for adding headers. However, in other parts of the same class, methods with no prefixes are used for setting values. For example version(v) is used for setting.

    I understand that there is a setHeader method for the headers, but I would recommend flip flopping the behavior to make it more intuitive. I think the "no-prefix" methods should be for setting, with "add" prefix methods used for adding. For example:

    Current com.github.mizosoft.methanol.internal.extensions.ResponseBuilder:

     public ResponseBuilder<T> header(String name, String value) {
        headersBuilder.add(name, value);
        return this;
      }
    
      public ResponseBuilder<T> setHeader(String name, String value) {
        headersBuilder.set(name, value);
        return this;
      }
    
      public ResponseBuilder<T> headers(HttpHeaders headers) {
        headersBuilder.addAll(headers);
        return this;
      }
    
    

    Suggested com.github.mizosoft.methanol.internal.extensions.ResponseBuilder:

      public ResponseBuilder<T> header(String name, String value) {
        headersBuilder.set(name, value);
        return this;
      }
    
      public ResponseBuilder<T> addHeader(String name, String value) {
        headersBuilder.add(name, value);
        return this;
      }
      
      public ResponseBuilder<T> headers(HttpHeaders headers) {
        clearHeaders();
        addHeaders(headers);
        return this;
      }
    
      public ResponseBuilder<T> addHeaders(HttpHeaders headers) {
        headersBuilder.addAll(headers);
        return this;
      }
    

    This primarily came about because we are used to OkHttp client which uses the following logic for setting all headers:

        /** Removes all headers on this builder and adds {@code headers}. */
        public Builder headers(Headers headers) {
          this.headers = headers.newBuilder();
          return this;
        }
    
    
    opened by regbo 4
  • Allow to override the filename used for a multipart part

    Allow to override the filename used for a multipart part

    Hi,

    I am using methanol for its multipart implementation.

    When using the partFile method, it would be convenient to be able to pass the filename to be used in the part, instead of using the name of the file itself.

    The only workaround is to rename or copy the file, which is not always possible.

    I can make a PR if desired :)

    EDIT: I just realized that I can also use formPart directly for the workaround, but it would be nice still to be able to do that in one go with partFile :)

    opened by victornoel 4
  • Improvements to `WritableBodyPublisher`

    Improvements to `WritableBodyPublisher`

    Right now users have to manually start a thread which writes into WritableBodyPublisher. This has two disadvantages:

    1. Data is written into the internal buffer even if the HTTP request fails or is never executed
    2. Users need to do manually close the stream/channel and handle exceptions

    Instead I would suggest to pass a callback to WritableBodyPublisher which is invoked in a new thread after the http client subscribes to the body handler. This thread will also close the publisher and handle exceptions. Users may also pass a custom executor to launch the thread.

    Current version:

    var requestBody = WritableBodyPublisher.create();
    var request = MutableRequest.POST("https://example.com", requestBody);
    
    CompletableFuture.runAsync(() -> {
      try (var outputStream  = requestBody.outputStream()) {
        outputStream .write(...);
      } catch (IOException ioe) {
        requestBody.closeExceptionally(ioe);
      }
    });
    
    client.sendAsync(request, BodyHandlers.discarding());
    

    Suggestion:

    // Optionally pass an executor using WritableBodyPublisher.withStream(executor, callback)
    var requestBody = WritableBodyPublisher.withStream(outputStream -> {
        // This is called in a new thread once the body publisher is subscribed to
        // It will close afterwards either normally or exceptionally
        outputStream.write(...)
    });
    var request = MutableRequest.POST("https://example.com", requestBody);
    
    client.sendAsync(request, BodyHandlers.discarding());
    
    opened by gpsfl 3
  • Deflate decoder doesn't handle raw deflate streams that aren't zlib-wrapped

    Deflate decoder doesn't handle raw deflate streams that aren't zlib-wrapped

    I use MoreBodyHandlers.decoding(BodyHandlers.ofString()), but it fails because 'nowrap' is not enabled for inflating. Could you please explain how to use the decoder with this option?

    opened by insticore 3
  • Enrich reported Progress in multipart upload request

    Enrich reported Progress in multipart upload request

    Currently, I can see that it is possible to attach a ProgressTracker to a MultPartBodyPublisher in order to track the progress of an upload, but the returned Progress object only contains the time/bytes information for the whole request.

    This is fine for single-file upload requests, but if the request involves uploading multiple files, it would be nice to provide the current file information, along with a per file progress value.

    opened by nemphys 3
  • Release 1.7.0 not yet on Maven Central

    Release 1.7.0 not yet on Maven Central

    Hi!

    Let be begin this message by saying that I love methanol! For us, it is an essential library to make the java.net.HttpClient usable. We mainly use methanol so that read-timeouts work even when the Response is an InputStream. This is not possible with the vanilla HttpClient.

    I just want to let you know that the latest release of methanol has not yet been published to Maven Central. I guess you probably already know about that, but I am writing this message just to make sure :)

    Thanks again for your work!

    opened by ChristianCiach 2
  • Response Variants

    Response Variants

    An HTTP response can nominate a set of headers pertaining to the initiating request, using the Vary header, to be matched against by the incoming request before being served by caches. This makes it possible to have multiple responses per URI (e.g. one for Accept-Encoding: gzip and another for Accept-Encoding: brotli for a Vary: Accept-Encoding). The prospect of implementing this seems nice due to the hypothetical increase in hit rates (the current behavior is to only keep the latest response). However, this makes it tricky to handle some cases like entry locking (lock edits on the entry scope or response scope?) & response updates (knowing which response to update).

    A major part of this is re-specifying the Store API to act more like a Map<String, List<Entry>>. Let's prioritize #50 to have a better idea of the implementation concerns of such a change.

    opened by mizosoft 0
Releases(v1.7.0)
  • v1.7.0(May 9, 2022)

    A full year has passed since the last Methanol release! Time truly flies. It's been difficult to find the time to cut this release due to my senior college year & other life circumstances, but here we are!

    • The Jackson adapter has been reworked to support the multitude of formats supported by Jackson, not only JSON (#45). That means you can now pass arbitrary ObjectMapper instances along with one or more MediaTypes describing their formats. For instance, here's a provider for a Jackson-based XML decoder.

      public class JacksonXmlDecoderProvider {
        private JacksonXmlDecoderProvider() {}
      
        public static BodyAdapter.Decoder provider() {
          return JacksonAdapterFactory.createDecoder(new XmlMapper(), MediaType.TEXT_XML);
        }
      }
      

      Binary formats (e.g. protocol buffers) usually require applying a schema for each type. ObjectReaderFacotry & ObjectWriterFactory have been added for this purpose. For instance, here's a provider for a protocol-buffers decoder. You'll need to know which types to expect beforehand.

       public class JacksonProtobufDecoderProvider {
        private JacksonProtobufDecoderProvider() {}
      
        public record Point(int x, int y) {}
      
        public static BodyAdapter.Decoder provider() throws IOException {
          var schemas = Map.of(
              TypeRef.from(Point.class),
              ProtobufSchemaLoader.std.parse(
                  """
                  message Point {
                    required int32 x = 1;
                    required int32 y = 2;
                  }
                  """), ...);
      
          // Apply the corresponding schema for each created ObjectReader
          ObjectReaderFactory readerFactory = 
              (mapper, type) -> mapper.readerFor(type.rawType()).with(schemas.get(type));
          return JacksonAdapterFactory.createDecoder(
              new ProtobufMapper(), readerFactory, MediaType.APPLICATION_X_PROTOBUF);
        }
      }
      
    • To avoid ambiguity, JacksonAdapterFactory::createDecoder & JacksonAdapterFactory::createEncoder that don't take an explicit MediaType have been deprecated and replaced with JacksonAdapterFactory::createJsonDecoder & JacksonAdapterFactory::createJsonEncoder respectively.

    • Added timeouts for receiving all response headers (#49). You can use these along with read timeouts to set more granular timing constraints for your requests when request timeouts are too strict.

      var client = Methanol.newBuilder()
          .headersTimeout(Duration.ofSeconds(30))
          .readTimeout(Duration.ofSeconds(30))
          ...
          .build()
      
    • Fix (#40): Methanol had a long-lived issue that made it difficult for service providers to work with custom JAR formats, particularly the one used by Spring Boot's executable JARs. Instead of the system classloader, Methanol now relies on the classloader that loaded the library itself for locating providers. This is not necessarily the system classloader as in the case with Spring Boot.

    • Fix (#46): ProgressTracker now returns MimeBodyPublisher if the body being tracked is itself a MimeBodyPublisher. This prevents "swallowing" the MediaType of such bodies.

    • Upgraded Jackson to 2.13.2.

    • Upgraded Gson to 2.9.0.

    • Upgraded Reactor to 3.4.17.

    Source code(tar.gz)
    Source code(zip)
Owner
Moataz Abdelnasser
Wakanda Forever
Moataz Abdelnasser
http-kit is a minimalist, event-driven, high-performance Clojure HTTP server/client library with WebSocket and asynchronous support

HTTP Kit A simple, high-performance event-driven HTTP client+server for Clojure CHANGELOG | API | current Break Version: [http-kit "2.5.3"] ; Publish

HTTP Client/Server for Clojure 2.3k Dec 31, 2022
Unirest in Java: Simplified, lightweight HTTP client library.

Unirest for Java Install With Maven: <!-- Pull in as a traditional dependency --> <dependency> <groupId>com.konghq</groupId> <artifactId>unire

Kong 2.4k Jan 5, 2023
Unirest in Java: Simplified, lightweight HTTP client library.

Unirest for Java Install With Maven: <!-- Pull in as a traditional dependency --> <dependency> <groupId>com.konghq</groupId> <artifactId>unire

Kong 2.4k Jan 5, 2023
Asynchronous Http and WebSocket Client library for Java

Async Http Client Follow @AsyncHttpClient on Twitter. The AsyncHttpClient (AHC) library allows Java applications to easily execute HTTP requests and a

AsyncHttpClient 6k Jan 8, 2023
Feign makes writing java http clients easier

Feign makes writing java http clients easier Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSocket. Feign's first goal

null 8.5k Dec 30, 2022
Google HTTP Client Library for Java

Google HTTP Client Library for Java Description Written by Google, the Google HTTP Client Library for Java is a flexible, efficient, and powerful Java

Google APIs 1.3k Jan 4, 2023
Java HTTP Request Library

Http Request A simple convenience library for using a HttpURLConnection to make requests and access the response. This library is available under the

Kevin Sawicki 3.3k Dec 30, 2022
Tiny, easily embeddable HTTP server in Java.

NanoHTTPD – a tiny web server in Java NanoHTTPD is a light-weight HTTP server designed for embedding in other applications, released under a Modified

NanoHttpd 6.5k Jan 5, 2023
A Java event based WebSocket and HTTP server

Webbit - A Java event based WebSocket and HTTP server Getting it Prebuilt JARs are available from the central Maven repository or the Sonatype Maven r

null 808 Jan 3, 2023
Feign makes writing java http clients easier

Feign makes writing java http clients easier Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSocket. Feign's first goal

null 8.5k Jan 1, 2023
Square’s meticulous HTTP client for the JVM, Android, and GraalVM.

OkHttp See the project website for documentation and APIs. HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP

Square 43.4k Jan 5, 2023
Share the chat messages across Minecraft Servers via HTTP backend powered by Spring Boot, this is the backend part of the project.

InterconnectedChat-Backend Share the chat messages across Minecraft Servers via HTTP backend powered by Spring Boot, this is the backend part of the p

贺兰星辰 3 Oct 6, 2021
Fast_Responder is a service that lets you quickly create an Http request responder

Fast_Responder is a service that lets you quickly create an Http request responder. The transponder can receive any request path configured and determine the request parameters according to your configuration to return different results. In addition to processing requests, the transponder can also make Http requests to any request address based on the latency, request headers, and parameters you configure. In essence, fast_responder is a dynamic mock service.

null 8 Jan 26, 2022
httpx - CLI to run HTTP file

httpx: CLI for run http file httpx is a CLI to execute requests from JetBrains Http File. How to install? Mac : brew install httpx-sh/tap/httpx Other

httpx 105 Dec 15, 2022
ssh, scp and sftp for java

sshj - SSHv2 library for Java To get started, have a look at one of the examples. Hopefully you will find the API pleasant to work with :) Getting SSH

Jeroen van Erp 2.2k Jan 4, 2023
AltiriaSmsJavaClient, the official Java client of Altiria

¡Atención! Este proyecto aún se encuentra en desarrollo. Pronto se publicará la versión final para su uso. Altiria, cliente SMS Java Altiria SMS Java

Altiria 4 Dec 5, 2022
Base classes and utilities for Java Carbyne Stack service clients

Carbyne Stack Java HTTP Client This project provides common functionality for the Java-based HTTP clients for the Carbyne Stack microservices. License

Carbyne Stack 5 Oct 15, 2022
GithubReleases4J - GitHub Releases for Java , based on GitHub RESTful API .

GithubReleases4J - GitHub Releases for Java , based on GitHub RESTful API .

Carm 5 Jun 27, 2022