Standalone Play WS, an async HTTP client with fluent API

Overview

Build Status Maven Javadocs

Play WS Standalone

Play WS is a powerful HTTP Client library, originally developed by the Play team for use with Play Framework. It uses AsyncHttpClient for HTTP client functionality and has no Play dependencies.

We've provided some documentation here on how to use Play WS in your app (without Play). For more information on how to use Play WS in Play, please refer to the Play documentation.

Getting Started

To get started, you can add play-ahc-ws-standalone as a dependency in SBT:

libraryDependencies += "com.typesafe.play" %% "play-ahc-ws-standalone" % "LATEST_VERSION"

Where you replace LATEST_VERSION with the version shown in this image: Latest released version.

This adds the standalone version of Play WS, backed by AsyncHttpClient. This library contains both the Scala and Java APIs, under play.api.libs.ws and play.libs.ws.

To add XML and JSON support using Play-JSON or Scala XML, add the following:

libraryDependencies += "com.typesafe.play" %% "play-ws-standalone-xml" % playWsStandaloneVersion
libraryDependencies += "com.typesafe.play" %% "play-ws-standalone-json" % playWsStandaloneVersion

Shading

Play WS uses shaded versions of AsyncHttpClient and OAuth Signpost, repackaged under the play.shaded.ahc and play.shaded.oauth package names, respectively. Shading AsyncHttpClient means that the version of Netty used behind AsyncHttpClient is completely independent of the application and Play as a whole.

Specifically, shading AsyncHttpClient means that there are no version conflicts introduced between Netty 4.0 and Netty 4.1 using Play WS.

NOTE: If you are developing play-ws and publishing shaded-asynchttpclient and shaded-oauth using sbt publishLocal, you need to be aware that updating ~/.ivy2/local does not overwrite ~/.ivy2/cache and so you will not see your updated shaded code until you remove it from cache. See http://eed3si9n.com/field-test for more details. This bug has been filed as https://github.com/sbt/sbt/issues/2687.

Shaded AHC Defaults

Because Play WS shades AsyncHttpClient, the default settings are also shaded and so do not adhere to the AHC documentation. This means that the settings in ahc-default.properties and the AsyncHttpClient system properties are prepended with play.shaded.ahc, for example the usePooledMemory setting in the shaded version of AsyncHttpClient is defined like this:

play.shaded.ahc.org.asynchttpclient.usePooledMemory=true

Typed Bodies

The type system in Play-WS has changed so that the request body and the response body can use richer types.

You can define your own BodyWritable or BodyReadable, but if you want to use the default out of the box settings, you can import the type mappings with the DefaultBodyReadables / DefaultBodyWritables.

Scala

import play.api.libs.ws.DefaultBodyReadables._
import play.api.libs.ws.DefaultBodyWritables._

More likely you will want the XML and JSON support:

import play.api.libs.ws.XMLBodyReadables._
import play.api.libs.ws.XMLBodyWritables._

or

import play.api.libs.ws.JsonBodyReadables._
import play.api.libs.ws.JsonBodyWritables._

To use a BodyReadable in a response, you must type the response explicitly:

import scala.concurrent.{ ExecutionContext, Future }

import play.api.libs.ws.StandaloneWSClient
import play.api.libs.ws.XMLBodyReadables._ // required

def handleXml(ws: StandaloneWSClient)(
  implicit ec: ExecutionContext): Future[scala.xml.Elem] =
  ws.url("...").get().map { response =>
    response.body[scala.xml.Elem]
  }

or using Play-JSON:

import scala.concurrent.{ ExecutionContext, Future }

import play.api.libs.json.JsValue
import play.api.libs.ws.StandaloneWSClient

import play.api.libs.ws.JsonBodyReadables._ // required

def handleJsonResp(ws: StandaloneWSClient)(
  implicit ec: ExecutionContext): Future[JsValue] =
  ws.url("...").get().map { response =>
    response.body[JsValue]
  }

Note that there is a special case: when you are streaming the response, then you should get the body as a Source:

import scala.concurrent.ExecutionContext
import akka.util.ByteString
import akka.stream.scaladsl.Source
import play.api.libs.ws.StandaloneWSClient

def useWSStream(ws: StandaloneWSClient)(implicit ec: ExecutionContext) =
  ws.url("...").stream().map { response =>
     val source: Source[ByteString, _] = response.bodyAsSource
     val _ = source // do something with source
  }

To POST, you should pass in a type which has an implicit class mapping of BodyWritable:

import scala.concurrent.ExecutionContext
import play.api.libs.ws.DefaultBodyWritables._ // required

def postExampleString(ws: play.api.libs.ws.StandaloneWSClient)(
  implicit ec: ExecutionContext) = {
  val stringData = "Hello world"
  ws.url("...").post(stringData).map { response => /* do something */ }
}

You can also define your own custom BodyReadable:

import play.api.libs.ws.BodyReadable
import play.api.libs.ws.ahc.StandaloneAhcWSResponse

case class Foo(body: String)

implicit val fooBodyReadable = BodyReadable[Foo] { response =>
  import play.shaded.ahc.org.asynchttpclient.{ Response => AHCResponse }
  val ahcResponse = response.asInstanceOf[StandaloneAhcWSResponse].underlying[AHCResponse]
  Foo(ahcResponse.getResponseBody)
}

or custom BodyWritable:

import akka.util.ByteString
import play.api.libs.ws.{ BodyWritable, InMemoryBody }

implicit val writeableOf_Foo: BodyWritable[Foo] = {
  // https://tools.ietf.org/html/rfc6838#section-3.2
  BodyWritable(foo => InMemoryBody(ByteString.fromString(foo.toString)), "application/vnd.company.category+foo")
}

Java

To use the default type mappings in Java, you should use the following:

import play.libs.ws.DefaultBodyReadables;
import play.libs.ws.DefaultBodyWritables;

followed by:

public class MyClient implements DefaultBodyWritables, DefaultBodyReadables {    
    public CompletionStage<String> doStuff() {
      return client.url("http://example.com").post(body("hello world")).thenApply(response ->
        response.body(string())
      );
    }
}

Note that there is a special case: when you are using a stream, then you should get the body as a Source:

class MyClass {
    public CompletionStage<Source<ByteString, NotUsed>> readResponseAsStream() {
        return ws.url(url).stream().thenApply(response ->
            response.bodyAsSource()
        );
    }
}

You can also post a Source:

class MyClass {
    public CompletionStage<String> doStuff() {
        Source<ByteString, NotUsed> source = fromSource();
        return ws.url(url).post(body(source)).thenApply(response ->
            response.body()
        );
    }
}

You can define a custom BodyReadable:

import play.libs.ws.ahc.*;
import play.shaded.ahc.org.asynchttpclient.Response;

class FooReadable implements BodyReadable<StandaloneWSResponse, Foo> {
    public Foo apply(StandaloneWSResponse response) {
        Response ahcResponse = (Response) response.getUnderlying();
        return Foo.serialize(ahcResponse.getResponseBody(StandardCharsets.UTF_8));
    }
}

You can also define your own custom BodyWritable:

public class MyClient {
    private BodyWritable<String> someOtherMethod(String string) {
      akka.util.ByteString byteString = akka.util.ByteString.fromString(string);
      return new DefaultBodyWritables.InMemoryBodyWritable(byteString, "text/plain");
    }
}

Instantiating a standalone client

The standalone client needs Akka to handle streaming data internally:

Scala

In Scala, the way to call out to a web service and close down the client:

package playwsclient

import akka.actor.ActorSystem
import akka.stream._
import play.api.libs.ws._
import play.api.libs.ws.ahc._

import scala.concurrent.Future

object ScalaClient {
  import DefaultBodyReadables._
  import scala.concurrent.ExecutionContext.Implicits._

  def main(args: Array[String]): Unit = {
    // Create Akka system for thread and streaming management
    implicit val system = ActorSystem()
    system.registerOnTermination {
      System.exit(0)
    }

    implicit val materializer = SystemMaterializer(system).materializer

    // Create the standalone WS client
    // no argument defaults to a AhcWSClientConfig created from
    // "AhcWSClientConfigFactory.forConfig(ConfigFactory.load, this.getClass.getClassLoader)"
    val wsClient = StandaloneAhcWSClient()

    call(wsClient)
      .andThen { case _ => wsClient.close() }
      .andThen { case _ => system.terminate() }
  }

  def call(wsClient: StandaloneWSClient): Future[Unit] = {
    wsClient.url("http://www.google.com").get().map { response =>
      val statusText: String = response.statusText
      val body = response.body[String]
      println(s"Got a response $statusText: $body")
    }
  }
}

You can also create the standalone client directly from an AsyncHttpClient instance:

object ScalaClient {
  def main(args: Array[String]): Unit = {
    // Use 
    import play.shaded.ahc.org.asynchttpclient._
    val asyncHttpClientConfig = new DefaultAsyncHttpClientConfig.Builder()
      .setMaxRequestRetry(0)
      .setShutdownQuietPeriod(0)
      .setShutdownTimeout(0).build
    val asyncHttpClient = new DefaultAsyncHttpClient(asyncHttpClientConfig)
    val wsClient = new StandaloneAhcWSClient(asyncHttpClient)
    /// ...
  }
}

This is useful when there is an AsyncHttpClient configuration option that is not available in the WS config layer.

Java

In Java the API is much the same:

package playwsclient;

import akka.actor.ActorSystem;
import akka.stream.*;
import com.typesafe.config.ConfigFactory;

import play.libs.ws.*;
import play.libs.ws.ahc.*;

public class JavaClient implements DefaultBodyReadables {
    private final StandaloneAhcWSClient client;
    private final ActorSystem system;

    public static void main(String[] args) {
        // Set up Akka materializer to handle streaming
        final String name = "wsclient";
        ActorSystem system = ActorSystem.create(name);
        system.registerOnTermination(() -> System.exit(0));
        Materializer materializer = SystemMaterializer.get(system).materializer();

        // Create the WS client from the `application.conf` file, the current classloader and materializer.
        StandaloneAhcWSClient ws = StandaloneAhcWSClient.create(
                AhcWSClientConfigFactory.forConfig(ConfigFactory.load(), system.getClass().getClassLoader()),
                materializer
        );

        JavaClient javaClient = new JavaClient(system, ws);
        javaClient.run();
    }

    JavaClient(ActorSystem system, StandaloneAhcWSClient client) {
        this.system = system;
        this.client = client;
    }

    public void run() {
        client.url("http://www.google.com").get()
                .whenComplete((response, throwable) -> {
                    String statusText = response.getStatusText();
                    String body = response.getBody(string());
                    System.out.println("Got a response " + statusText);
                })
                .thenRun(() -> {
                    try {
                        client.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                })
                .thenRun(system::terminate);
    }
}

Likewise, you can provide the AsyncHttpClient client explicitly from configuration:

public class JavaClient implements DefaultBodyReadables {
     public static void main(String[] args) { 
        // ...
        // Set up AsyncHttpClient directly from config
        AsyncHttpClientConfig asyncHttpClientConfig =
            new DefaultAsyncHttpClientConfig.Builder()
                .setMaxRequestRetry(0)
                .setShutdownQuietPeriod(0)
                .setShutdownTimeout(0)
                .build();
        AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient(asyncHttpClientConfig);
    
        // Set up WSClient instance directly from asynchttpclient.
        WSClient client = new AhcWSClient(asyncHttpClient, materializer);
        // ...
    }
}

Caching

Play WS implements HTTP Caching through CachingAsyncHttpClient, AhcHTTPCache and CacheControl, a minimal HTTP cache management library in Scala.

To create a standalone AHC client that uses caching, pass in an instance of AhcHttpCache with a cache adapter to the underlying implementation. For example, to use Caffeine as the underlying cache, you could use the following:

import scala.concurrent.Future
import java.util.concurrent.TimeUnit
import com.github.benmanes.caffeine.cache.{ Caffeine, Ticker }

import play.api.libs.ws.ahc.StandaloneAhcWSClient
import play.api.libs.ws.ahc.cache.{
  AhcHttpCache, Cache, EffectiveURIKey, ResponseEntry
}

class CaffeineHttpCache extends Cache {
  val underlying = Caffeine.newBuilder()
    .ticker(Ticker.systemTicker())
    .expireAfterWrite(365, TimeUnit.DAYS)
    .build[EffectiveURIKey, ResponseEntry]()

  def remove(key: EffectiveURIKey) =
    Future.successful(Option(underlying.invalidate(key)))

  def put(key: EffectiveURIKey, entry: ResponseEntry) =
    Future.successful(underlying.put(key, entry))

  def get(key: EffectiveURIKey) =
    Future.successful(Option(underlying getIfPresent key ))

  def close(): Unit = underlying.cleanUp()
}

def withCache(implicit m: akka.stream.Materializer): StandaloneAhcWSClient = {
  implicit def ec = m.executionContext

  val cache = new CaffeineHttpCache()
  StandaloneAhcWSClient(httpCache = Some(new AhcHttpCache(cache)))
}

There are a number of guides that help with putting together Cache-Control headers:

License

Play WS is licensed under the Apache license, version 2. See the LICENSE file for more information.

Comments
  • [fix] don't use AHC cookie store

    [fix] don't use AHC cookie store

    The WSRequest withCookies method suggests all cookies will be discarded. This is not the case if the underlying http client has a cookie store. Play ws also does not know about those cookies so for example the AhcCurlRequestLogger does not log these cookies.

    opened by bomgar 16
  • Add withUrl(String)

    Add withUrl(String)

    Pull Request Checklist

    • [x] Have you read through the contributor guidelines?
    • [x] Have you signed the Typesafe CLA?
    • [x] Have you squashed your commits?
    • [x] Have you added copyright headers to new files?
    • [x] Have you checked that both Scala and Java APIs are updated? (see below)
    • [x] Have you updated the documentation for both Scala and Java sections?
    • [x] Have you added tests for any changed functionality?

    Fixes

    Fixes #264

    Purpose

    Adds support in the scala API for changing the url of the WSRequest.

    Background Context

    Like @gmethvin, I've encountered a couple use cases where I'd like to modify the URL of an existing WSRequest.

    1. Fixing URIs In my WSRequestFilters, I'd like to make use of WSRequest.uri, but it's unsafe (see #267). Today, I have a WSRequestFilter to sanitize the entire request prior to subsequent WSRequestFilters touching WSRequest.uri. In order to strip out query params and any other garbage from the URL, my WSRequestFilter has to rebuild the whole request from scratch. Additionally, it has to require a WSClient to do the job (wsClient.url()), which I hope is the same WSClient that originally used to build the request. Ugh!

    2. Changing hostnames We're cursed with https://github.com/mesosphere/marathon-lb, which sometimes fails to route requests. I'd love to be able to have a WSRequestFilter I can use to bypass that layer and rewrite https://pray-marathon-lb-works.example.com/foo to https://10.0.0.25/foo.

    opened by htmldoug 16
  • ssl-config-core 0.6.0 (was 0.4.3) + cachecontrol 2.1.0

    ssl-config-core 0.6.0 (was 0.4.3) + cachecontrol 2.1.0

    Updates com.typesafe:ssl-config-core from 0.4.3 to 0.5.0. GitHub Release Notes - Version Diff

    I'll automatically update this PR to resolve conflicts as long as you don't change it yourself.

    If you'd like to skip this version, you can just close this PR. If you have any feedback, just mention me in the comments below.

    Configure Scala Steward for your repository with a .scala-steward.conf file.

    Have a fantastic day writing Scala!

    Files still referring to the old version number

    The following files still refer to the old version number (0.4.3). You might want to review and update them manually.

    project/plugins.sbt
    
    Ignore future updates

    Add this to your .scala-steward.conf file to ignore future updates of this dependency:

    updates.ignore = [ { groupId = "com.typesafe", artifactId = "ssl-config-core" } ]
    

    labels: library-update, semver-minor, old-version-remains

    opened by scala-steward 15
  • add new AHC parameter: connectionPoolCleanerPeriod

    add new AHC parameter: connectionPoolCleanerPeriod

    Pull Request Checklist

    • [x] Have you read through the contributor guidelines?
    • [x] Have you signed the Typesafe CLA?
    • [x] Have you squashed your commits?
    • [x] Have you added copyright headers to new files?
    • [x] Have you checked that both Scala and Java APIs are updated?
    • [x] Have you updated the documentation for both Scala and Java sections?
    • [x] Have you added tests for any changed functionality?

    Purpose

    add new AHC parameter: connectionPoolCleanerPeriod

    Background Context

    Sometimes we need to populate idleConnectionInPoolTimeout together with connectionPoolCleanerPeriod

    References

    Are there any relevant issues / PRs / mailing lists discussions?

    opened by maxcellent 13
  • Make the client mockable with Java mocking frameworks

    Make the client mockable with Java mocking frameworks

    Play WS Version (2.5.x / etc)

    1.0.0-M8

    API (Scala / Java / Neither / Both)

    Scala

    Expected Behavior

    It should be able to mock the client with Java mocking libraries like mockito, EasyMock or JMock.

    Actual Behavior

    The following code doesn't compile:

    val wsRequest = mock[WSRequest]
    val wsResponse = mock[WSResponse]
    
    wsResponse.json throws new RuntimeException("Unexpected character ('<' (code 60))")
    wsResponse.body returns "<html></html>"
    wsRequest.withHeaders(any) returns wsRequest
    wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
    
    /home/travis/build/mohiva/play-silhouette/silhouette/test/com/mohiva/play/silhouette/impl/providers/OAuth2ProviderSpec.scala:274: type mismatch;
    [error]  found   : this.wsRequest.type (with underlying type play.api.libs.ws.WSRequest)
    [error]  required: this.wsRequest.Self
    [error]       wsRequest.withHeaders(any) returns wsRequest
    
    /home/travis/build/mohiva/play-silhouette/silhouette/test/com/mohiva/play/silhouette/impl/providers/OAuth2ProviderSpec.scala:275: type mismatch;
    [error]  found   : this.wsResponse.type (with underlying type play.api.libs.ws.WSResponse)
    [error]  required: this.wsRequest.Response
    [error]       wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
    

    Reproducible Test Case

    https://github.com/mohiva/play-silhouette/pull/509

    Links

    https://groups.google.com/forum/#!topic/play-framework/mYPRPbfa8Uk

    Notes

    Both Scala testing libraries support mocking with Java mocking frameworks. ScalaTest supports one Scala and three Java mocking frameworks. Specs2 provides syntactic sugar for mockito. This shows that using Java mocking frameworks in the Scala world is a preferred method.

    Switching to play-mockws as suggested by @wsargent , isn't a good solution for a play related library, because then the library is bound to the release cycle of that play related dependency.

    opened by akkie 13
  • MEGA ticket for Akka Http backend related tickets

    MEGA ticket for Akka Http backend related tickets

    Work-in-progress branch and PR can be found here: https://github.com/playframework/play-ws/pull/205

    List of topics that are currently missing:

    • [ ] #213 configuration
    • [ ] #214 proxy support when using HTTP https://github.com/akka/akka-http/issues/115
    • [ ] #215 follow redirects https://github.com/akka/akka-http/issues/195
    • [ ] #216 proper request timeout support. Now it is implemented using future timeouts. https://github.com/akka/akka-http/issues/42
    • [ ] #217 caching support
    • [ ] #218 signature calculator support
    • [ ] authentication
      • [x] BASIC
      • [ ] #219 DIGEST
      • [ ] #220 NTLM
      • [ ] #221 SPNEGO
      • [ ] #222 KERBEROS
    opened by 2m 12
  • WSRequest: Normalize URL

    WSRequest: Normalize URL

    Pull Request Checklist

    • [x] Have you read through the contributor guidelines?
    • [x] Have you signed the Typesafe CLA?
    • [x] Have you squashed your commits?
    • [x] Have you added copyright headers to new files?
    • [x] Have you checked that both Scala and Java APIs are updated?
    • [x] Have you updated the documentation for both Scala and Java sections?
    • [x] Have you added tests for any changed functionality?

    Fixes

    Fixes #267

    Purpose

    Ensure that StandaloneWSRequest.uri does not return invalid values or throw, by normalizing the url: String when a StandaloneWSRequest is constructed. Same approach is used by the java implementation.

    Background Context

    This approach follows the "tolerant" recommendation, https://github.com/playframework/play-ws/issues/267#issuecomment-419266583.

    Ensures:

    1. StandaloneWSRequest.url path is encoded.
    2. Any query params present in the URL are moved to StandaloneWSRequest.queryString.

    Implementation is based upon a WSRequestFilter-like decorator I've been using in production since June 2018. An advantage to fixing the request right away is that request.uri can be invoked safely sooner in the process.

    Caveats

    Normalization can still be bypassed by:

    • StandaloneAhcWSClient.copy(url = ">^..^<", ...)
    • new StandaloneAhcWSRequest(client, ">^..^<", ...)
    status:backlog 
    opened by htmldoug 11
  • Upgrade async-http-client to 2.4.4 to expose client connection stats

    Upgrade async-http-client to 2.4.4 to expose client connection stats

    Couldn't find a way to update branch name in the other PR, so had to create a new PR

    Pull Request Checklist

    • [Y] Have you read through the contributor guidelines?
    • [Y] Have you signed the Typesafe CLA?
    • [Y] Have you squashed your commits?
    • [N/A] Have you added copyright headers to new files?
    • [Y] Have you checked that both Scala and Java APIs are updated?
    • [Y] Have you updated the documentation for both Scala and Java sections?
    • [Y] Have you added tests for any changed functionality?

    Fixes

    Upgrade Async-Http-Client to 2.4.4

    Purpose

    Have access to connection stats through new class exposed from AsyncHttpClient

    Background Context

    One major change around Cookie specific codes from Async-Http-Client is moved to Netty and there are now 2 different decoders (STRICT and LAX) so that we had to make that configurable.

    References

    • https://discuss.lightbend.com/t/upgrade-async-http-client/752
    • CHANGES in the Async-Http-Client : https://github.com/AsyncHttpClient/async-http-client/blob/master/CHANGES.md

    Please refer to https://github.com/playframework/play-ws/pull/252 for previous comments

    opened by fatihi-sky 11
  • Exceptions when using together with AsyncHttpClient

    Exceptions when using together with AsyncHttpClient

    Sorry for cross-posting. I've written down the details of this issue at: https://github.com/playframework/playframework/issues/7056

    It looks like (at least in my Play 2.6.0-M1 app) that the shaded AsyncHttpClientDefaults class reads the ahc-default.properties that are pulled in from the non-shaded version on my classpath.

    The issue probably comes from https://github.com/AsyncHttpClient/async-http-client/blob/async-http-client-project-2.0.27/client/src/main/java/org/asynchttpclient/config/AsyncHttpClientConfigHelper.java#L47

    Where the current ThreadContext Classloader is used to lookup the properties instead a classloader based on the current class (which would then find the ahc-default.properties with the play.shaded prefix.

    opened by domdorn 11
  • Add Scala 3 support

    Add Scala 3 support

    would a volunteer like to attempt this? it would be helpful even just to have a failing draft PR that shows where the next roadblock is, and then we could chip away it from there

    I published ssl-config and cachecontrol today for Scala 3, so that helps

    #590 should be merged before any attempt is made at this

    opened by SethTisue 10
  • fix: make query param order deterministic when adding params

    fix: make query param order deterministic when adding params

    Pull Request Checklist

    • [x] Have you read through the contributor guidelines?
    • [x] Have you signed the Typesafe CLA?
    • [x] Have you squashed your commits?
    • [ ] Have you added copyright headers to new files?
    • [ ] Have you checked that both Scala and Java APIs are updated?
    • [ ] Have you updated the documentation for both Scala and Java sections?
    • [x] Have you added tests for any changed functionality?

    Fixes

    Fixes #48

    Purpose

    Ensure deterministic query param order when adding params through the WSRequest interface. This does not address order when adding to a query param that already contains a value. for example

    req.addQueryParam("p1", "v1")
    req.addQueryParam("p2", "v2")
    req.addQueryParam("p1, "v3")
    

    will yield p1=v1&p1=v3&p2=v2 which is still not perfect but is at least deterministic

    Background Context

    I wanted to make the minimal code change to achieve deterministic behaviour. When generating digital signatures of urls (for example of an Authorization header) it's important to have a deterministic order to be able to correctly compute the digest.

    References

    https://github.com/playframework/playframework/pull/6884 https://github.com/playframework/play-ws/pull/102

    opened by bpossolo 10
  • Got

    Got "Digest algorithm not supported: md5" on try use DIGEST

    As I understand it, when calling the ws.url().withAuth(username, password, WSAuthScheme.DIGEST) function, the MD5 encryption algorithm is selected by default, but it is not supported and I get an error. How can I force DIGEST to use SHA or allow configuration to use MD5?

    The project uses:

    scala - 2.13.8
    play  - 2.8.16
    jvm   - openjdk 11.0.16.1 2022-08-12 LTS
    

    **UPD error log

    [debug] p.s.a.o.a.n.r.NettyRequestSender - Digest algorithm not supported: md5
    java.lang.UnsupportedOperationException: Digest algorithm not supported: md5
    at play.shaded.ahc.org.asynchttpclient.Realm$Builder.ha1(Realm.java:504)
    at play.shaded.ahc.org.asynchttpclient.Realm$Builder.newResponse(Realm.java:542)
    at play.shaded.ahc.org.asynchttpclient.Realm$Builder.build(Realm.java:565)
    at play.shaded.ahc.org.asynchttpclient.netty.handler.intercept.Unauthorized401Interceptor.exitAfterHandling401(Unauthorized401Interceptor.java:117)
    at play.shaded.ahc.org.asynchttpclient.netty.handler.intercept.Interceptors.exitAfterIntercept(Interceptors.java:89)
    at play.shaded.ahc.org.asynchttpclient.netty.handler.HttpHandler.handleHttpResponse(HttpHandler.java:78)
    at play.shaded.ahc.org.asynchttpclient.netty.handler.HttpHandler.handleRead(HttpHandler.java:140)
    at play.shaded.ahc.org.asynchttpclient.netty.handler.AsyncHttpClientHandler.channelRead(AsyncHttpClientHandler.java:78)
    at play.shaded.ahc.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
    at play.shaded.ahc.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
    [debug] p.s.a.o.a.n.c.ChannelManager - Closing Channel [**secure ip]
    [debug] p.s.a.o.a.n.h.HttpHandler - Unexpected I/O exception on channel [id: 0x27d4ea64, **secure ip ! **secure ip]
    
    opened by mprosvitlyk 0
  • [feature] Add support for `SameSite` attribute in `WSCookie`

    [feature] Add support for `SameSite` attribute in `WSCookie`

    The SameSite cookie attribute is widespread, see e.g. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#browser_compatibility.

    It would be great if play-ws helped me consume SameSite attributes, e.g. by having a sameSite field on WSCookie.

    opened by jonaskoelker-jypo 0
  • Null pointer error in `play.api.libs.ws.ahc.CookieBuilder` if cookie has no value

    Null pointer error in `play.api.libs.ws.ahc.CookieBuilder` if cookie has no value

    In my app I construct a WSRequest and call .stream() on it (this is the Scala API). It's talking to an in-house service, which responds with a Set-Cookie header with a cookie name but no cookie value. In my repro setup I use a mock response with a line saying Set-Cookie: absent-value;. In both cases I get a null pointer exception.

    I tracked it down:

    • When I touch the cookies I hit this: https://github.com/playframework/play-ws/blob/d06f8b15b2564384c0754711b8cb2dbb384311f0/play-ahc-ws-standalone/src/main/scala/play/api/libs/ws/ahc/StreamedResponse.scala#L56
    • This in turns goes here: https://github.com/playframework/play-ws/blob/d06f8b15b2564384c0754711b8cb2dbb384311f0/play-ahc-ws-standalone/src/main/scala/play/api/libs/ws/ahc/CookieBuilder.scala#L21
    • Since the cookie value is absent, the underlying cookie builder returns null
    • Then Some(c) = Some(null) acts just like c = null (in the cookie builder)
    • Then asCookie(null) is called, which tries to access (null).name which throws the null pointer exception.

    I believe Some(c) = Option(<the same expression as before>) will make the null pointer exception not happen, but it's not obvious to me that this is the right design.

    It's just what I did as a workaround: I made a wrapper around WSRepsonse which forwards everything to the underlying response, except it diverts the code path leading to this buggy line onto a copy-pasted-and-the-fixed version which says Option instead of Some on this line—plus some incidental and (I believe) unrelated changes.

    I'm not sure what behavior to expect, given that value-free cookie headers do not conform to any HTTP standard I could find—not that I looked all day—but I definitely don't expect a null pointer exception.

    Here's sbt dependencyTree | grep ws | grep -v aws | cut -f2- -d'-' | sort -u, just in case it helps. My above links are to the most recent version of each of the linked files, as far as I can find out; they look like differently indented versions of the source my LSP/metals setup jumps to.

    com.typesafe.play:play-ahc-ws-standalone_2.13:2.1.7 [S]
    com.typesafe.play:play-ahc-ws_2.13:2.8.11 (evicted by: 2.8.13)
    com.typesafe.play:play-ahc-ws_2.13:2.8.13 [S]
    com.typesafe.play:play-ws-standalone-json_2.13:2.1.7 [S]
    com.typesafe.play:play-ws-standalone-xml_2.13:2.1.7 [S]
    com.typesafe.play:play-ws-standalone_2.13:2.1.7 [S]
    com.typesafe.play:play-ws_2.13:2.8.13 [S]
    

    API: Scala. We also have a bit of java stuff lying around but I don't think it interacts with this bug.

    OS/Java:

    $ uname -a
    Linux <hostname_withheld> 5.4.0-122-generic #138-Ubuntu SMP Wed Jun 22 15:00:31 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
    
    $ java -version
    openjdk version "11.0.16" 2022-07-19
    OpenJDK Runtime Environment (build 11.0.16+8-post-Ubuntu-0ubuntu120.04)
    OpenJDK 64-Bit Server VM (build 11.0.16+8-post-Ubuntu-0ubuntu120.04, mixed mode, sharing)
    
    opened by jonaskoelker-jypo 0
  • Source code for shaded jars are not properly published along with the shaded jars

    Source code for shaded jars are not properly published along with the shaded jars

    The problem is when attaching debugger with a running process using any shaded library. The non-shaded source code won't bind with the shaded classes that belong to the shaded packages such as

    package play.shaded.ahc.io.netty.channel.nio;
    ...
    public abstract class AbstractNioChannel extends AbstractChannel {
    

    The source code is still 266 bytes, see, https://repo1.maven.org/maven2/com/typesafe/play/shaded-asynchttpclient/2.1.3/

    So any plan to correctly publish shaded sources?

    A workaround in case someone needs it

    $ cd netty
    $ mkdir -p transport/src/main/java/play/shaded/ahc
    $ cp -a transport/src/main/java/io transport/src/main/java/play/shaded/ahc/.
    $ find transport/src/main/java/play/shaded/ahc -name "*.java" -exec sed -i 's#package io\.#package play.shaded.ahc.io.#' {} \; -exec sed -i 's#import io\.#import play.shaded.ahc.io.#' {} \; -exec sed -i 's#import static io\.#import static play.shaded.ahc.io.#' {} \;
    

    Originally posted by @jhooda in https://github.com/playframework/play-ws/issues/85#issuecomment-1130406025

    opened by jhooda 0
  • IOException Connection closed after many requests

    IOException Connection closed after many requests

    Hello! We are using play ws to execute requests on a highly loaded system. After running the system for about 30 - 40 minutes, we get an IOException Connection closed error and all further requests stop being executed until the system is restarted. Perhaps you know this problem and you tell me what we are doing wrong?

    We use - Scala version: 2.11.6 Play version: 2.4.11 Akka version: 2.4.0 SBT version: 0.13.8

    Error stack trace -

    java.io.IOException: Closed
    	at com.ning.http.client.providers.netty.request.NettyRequestSender.sendRequest(NettyRequestSender.java:98) ~[com.ning.async-http-client-1.9.40.jar:na]
    	at com.ning.http.client.providers.netty.NettyAsyncHttpProvider.execute(NettyAsyncHttpProvider.java:87) ~[com.ning.async-http-client-1.9.40.jar:na]
    	at com.ning.http.client.AsyncHttpClient.executeRequest(AsyncHttpClient.java:506) ~[com.ning.async-http-client-1.9.40.jar:na]
    	at play.api.libs.ws.ning.NingWSClient.executeRequest(NingWS.scala:47) ~[com.typesafe.play.play-ws_2.11-2.4.11.jar:2.4.11]
    	at play.api.libs.ws.ning.NingWSRequest.execute(NingWS.scala:312) ~[com.typesafe.play.play-ws_2.11-2.4.11.jar:2.4.11]
    	at play.api.libs.ws.ning.NingWSRequest.execute(NingWS.scala:128) ~[com.typesafe.play.play-ws_2.11-2.4.11.jar:2.4.11]
    	at play.api.libs.ws.WSRequest$class.post(WS.scala:455) ~[com.typesafe.play.play-ws_2.11-2.4.11.jar:2.4.11]
    	at play.api.libs.ws.ning.NingWSRequest.post(NingWS.scala:81) ~[com.typesafe.play.play-ws_2.11-2.4.11.jar:2.4.11]
    	at ru.alfatell.webregistry.XmlExecutionContext$WebExecutor.callInternal(XmlExecutor.scala:338) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at ru.alfatell.webregistry.XmlExecutor$class.call(XmlExecutor.scala:199) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at ru.alfatell.webregistry.XmlExecutor$class.perform(XmlExecutor.scala:119) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at ru.alfatell.webregistry.XmlExecutionContext$WebExecutor.perform(XmlExecutor.scala:287) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at ru.alfatell.webregistry.ExternalApiGateway$$anonfun$execute$1$$anonfun$apply$12.apply(ExternalApiGateway.scala:111) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at ru.alfatell.webregistry.ExternalApiGateway$$anonfun$execute$1$$anonfun$apply$12.apply(ExternalApiGateway.scala:90) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at scala.util.Try$.apply(Try.scala:192) ~[org.scala-lang.scala-library-2.11.8.jar:na]
    	at ru.alfatell.webregistry.ExternalApiGateway$$anonfun$execute$1.apply(ExternalApiGateway.scala:90) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at ru.alfatell.webregistry.ExternalApiGateway$$anonfun$execute$1.apply(ExternalApiGateway.scala:89) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at scala.util.Success.flatMap(Try.scala:231) ~[org.scala-lang.scala-library-2.11.8.jar:na]
    	at ru.alfatell.webregistry.ExternalApiGateway$.execute(ExternalApiGateway.scala:89) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at controllers.tmp.ExtApiMain$$anonfun$xml$1$$anonfun$apply$1$$anonfun$4.apply(ExtApiMain.scala:24) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at controllers.tmp.ExtApiMain$$anonfun$xml$1$$anonfun$apply$1$$anonfun$4.apply(ExtApiMain.scala:24) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at controllers.tmp.ExtApiSecurity$class.callerAuthorize(ExtApiSecurity.scala:158) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at controllers.ExternalApi$.callerAuthorize(ExternalApi.scala:34) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at controllers.tmp.ExtApiMain$$anonfun$xml$1$$anonfun$apply$1.apply(ExtApiMain.scala:24) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at controllers.tmp.ExtApiMain$$anonfun$xml$1$$anonfun$apply$1.apply(ExtApiMain.scala:21) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at controllers.tmp.ExtApiSecurity$class.withCtx(ExtApiSecurity.scala:121) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at controllers.ExternalApi$.withCtx(ExternalApi.scala:34) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at controllers.tmp.ExtApiMain$$anonfun$xml$1.apply(ExtApiMain.scala:21) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at controllers.tmp.ExtApiMain$$anonfun$xml$1.apply(ExtApiMain.scala:20) ~[ru.alfatell.web-registry-portal-1.1.1-sans-externalized.jar:na]
    	at play.api.mvc.Action$.invokeBlock(Action.scala:533) ~[com.typesafe.play.play_2.11-2.4.11.jar:2.4.11]
    	at play.api.mvc.Action$.invokeBlock(Action.scala:530) ~[com.typesafe.play.play_2.11-2.4.11.jar:2.4.11]
    	at play.api.mvc.ActionBuilder$$anon$1.apply(Action.scala:493) ~[com.typesafe.play.play_2.11-2.4.11.jar:2.4.11]
    	at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:105) ~[com.typesafe.play.play_2.11-2.4.11.jar:2.4.11]
    	at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:105) ~[com.typesafe.play.play_2.11-2.4.11.jar:2.4.11]
    	at play.utils.Threads$.withContextClassLoader(Threads.scala:21) ~[com.typesafe.play.play_2.11-2.4.11.jar:2.4.11]
    	at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:104) ~[com.typesafe.play.play_2.11-2.4.11.jar:2.4.11]
    	at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:103) ~[com.typesafe.play.play_2.11-2.4.11.jar:2.4.11]
    	at scala.Option.map(Option.scala:146) ~[org.scala-lang.scala-library-2.11.8.jar:na]
    	at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:103) ~[com.typesafe.play.play_2.11-2.4.11.jar:2.4.11]
    	at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:96) ~[com.typesafe.play.play_2.11-2.4.11.jar:2.4.11]
    	at play.api.libs.iteratee.Iteratee$$anonfun$mapM$1.apply(Iteratee.scala:524) ~[com.typesafe.play.play-iteratees_2.11-2.4.11.jar:2.4.11]
    	at play.api.libs.iteratee.Iteratee$$anonfun$mapM$1.apply(Iteratee.scala:524) ~[com.typesafe.play.play-iteratees_2.11-2.4.11.jar:2.4.11]
    	at play.api.libs.iteratee.Iteratee$$anonfun$flatMapM$1.apply(Iteratee.scala:560) ~[com.typesafe.play.play-iteratees_2.11-2.4.11.jar:2.4.11]
    	at play.api.libs.iteratee.Iteratee$$anonfun$flatMapM$1.apply(Iteratee.scala:560) ~[com.typesafe.play.play-iteratees_2.11-2.4.11.jar:2.4.11]
    	at play.api.libs.iteratee.Iteratee$$anonfun$flatMap$1$$anonfun$apply$14.apply(Iteratee.scala:537) ~[com.typesafe.play.play-iteratees_2.11-2.4.11.jar:2.4.11]
    	at play.api.libs.iteratee.Iteratee$$anonfun$flatMap$1$$anonfun$apply$14.apply(Iteratee.scala:537) ~[com.typesafe.play.play-iteratees_2.11-2.4.11.jar:2.4.11]
    	at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24) ~[org.scala-lang.scala-library-2.11.8.jar:na]
    	at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24) ~[org.scala-lang.scala-library-2.11.8.jar:na]
    	at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:39) [com.typesafe.akka.akka-actor_2.11-2.4.0.jar:na]
    	at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:399) [com.typesafe.akka.akka-actor_2.11-2.4.0.jar:na]
    	at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) [org.scala-lang.scala-library-2.11.8.jar:na]
    	at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) [org.scala-lang.scala-library-2.11.8.jar:na]
    	at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) [org.scala-lang.scala-library-2.11.8.jar:na]
    	at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) [org.scala-lang.scala-library-2.11.8.jar:na]
    
    opened by pavlovaleksei 0
Releases(2.2.0-M2)
tasks, async await, actors and channels for java

AsyncUtils tasks, async, await, actors and channels for java This project tries to explore several approaches to simplify async/concurrent programming

Michael Hoffer 6 Dec 1, 2022
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 9, 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 Dec 31, 2022
Telegram API Client and Telegram BOT API Library and Framework in Pure java.

Javagram Telegram API Client and Telegram Bot API library and framework in pure Java. Hello Telegram You can use Javagram for both Telegram API Client

Java For Everything 3 Oct 17, 2021
The Java gRPC implementation. HTTP/2 based RPC

gRPC-Java - An RPC library and framework gRPC-Java works with JDK 7. gRPC-Java clients are supported on Android API levels 16 and up (Jelly Bean and l

grpc 10.2k Jan 1, 2023
Magician is an asynchronous non-blocking network protocol analysis package, supports TCP, UDP protocol, built-in Http, WebSocket decoder

An asynchronous non-blocking network protocol analysis package Project Description Magician is an asynchronous non-blocking network protocol analysis

贝克街的天才 103 Nov 30, 2022
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 Dec 23, 2022
HTTP Server Model made in java

SimplyJServer HTTP Server Model made in java Features Fast : SimplyJServer is 40%-60% faster than Apache, due to it's simplicity. Simple to implement

Praudyogikee for Advanced Technology 2 Sep 25, 2021
TCP/UDP client/server library for Java, based on Kryo

KryoNet can be downloaded on the releases page. Please use the KryoNet discussion group for support. Overview KryoNet is a Java library that provides

Esoteric Software 1.7k Jan 2, 2023
Proteus Java Client

Netifi Proteus Java This project has been moved to https://github.com/netifi/netifi-java Build from Source Run the following Gradle command to build t

netifi-proteus 42 Nov 20, 2020
Socket.IO Client Implementation in Java

Socket.IO-Client for Java socket.io-java-client is an easy to use implementation of socket.io for Java. It uses Weberknecht as transport backend, but

Enno Boland 946 Dec 21, 2022
Full-featured Socket.IO Client Library for Java, which is compatible with Socket.IO v1.0 and later.

Socket.IO-client Java This is the Socket.IO Client Library for Java, which is simply ported from the JavaScript client. See also: Android chat demo en

Socket.IO 5k Jan 4, 2023
BAIN Social is a Fully Decentralized Server/client system that utilizes Concepts pioneered by I2P, ToR, and PGP to create a system which bypasses singular hosts for data while keeping that data secure.

SYNOPSIS ---------------------------------------------------------------------------------------------------- Welcome to B.A.I.N - Barren's A.I. Natio

Barren A.I. Wolfsbane 14 Jan 11, 2022
IoT Platform, Device management, data collection, processing and visualization, multi protocol, rule engine, netty mqtt client

GIoT GIoT: GIoT是一个开源的IoT平台,支持设备管理、物模型,产品、设备管理、规则引擎、多种存储、多sink、多协议(http、mqtt、tcp,自定义协议)、多租户管理等等,提供插件化开发 Documentation Quick Start Module -> giot-starte

gerry 34 Sep 13, 2022
Book Finder application is a client-server application (gRPC) for educational purposes.

Book-Finder Book Finder application is a client-server application (gRPC) for educational purposes. Instalation These projects (Client/Server) are Mav

Mihai-Lucian Rîtan 21 Oct 27, 2022
A barebones WebSocket client and server implementation written in 100% Java.

Java WebSockets This repository contains a barebones WebSocket server and client implementation written in 100% Java. The underlying classes are imple

Nathan Rajlich 9.5k Dec 30, 2022
Realtime Client Server Framework for the JVM, supporting WebSockets with Cross-Browser Fallbacks

Welcome to Atmosphere: The Event Driven Framework supporting WebSocket and HTTP The Atmosphere Framework contains client and server side components fo

Atmosphere Framework 3.6k Jan 3, 2023
A small java project consisting of Client and Server, that communicate via TCP/UDP protocols.

Ninja Battle A small java project consisting of Client and Server, that communicate via TCP/UDP protocols. Client The client is equipped with a menu i

Steliyan Dobrev 2 Jan 14, 2022