0
# Ning HTTP Client Implementation
1
2
Ning AsyncHttpClient-based implementation providing the actual HTTP transport layer with advanced configuration options. This is the default implementation used by Play WS for HTTP operations.
3
4
## Capabilities
5
6
### Ning WS Client
7
8
Main Ning-based WSClient implementation.
9
10
```scala { .api }
11
/**
12
* WSClient implementation using Ning AsyncHttpClient
13
* @param config AsyncHttpClient configuration
14
*/
15
case class NingWSClient(config: AsyncHttpClientConfig) extends WSClient {
16
def underlying[T]: T
17
def url(url: String): WSRequest
18
def close(): Unit
19
}
20
21
/**
22
* Ning WSClient factory
23
*/
24
object NingWSClient {
25
/** Create client with default configuration */
26
def apply(): NingWSClient
27
28
/** Create client with custom configuration */
29
def apply(config: NingWSClientConfig): NingWSClient
30
}
31
```
32
33
**Basic Ning Client Usage:**
34
35
```scala
36
import play.api.libs.ws.ning._
37
38
// Create client with default configuration
39
val client = NingWSClient()
40
41
// Use the client
42
val response = client.url("https://api.example.com/data").get()
43
44
// Always close the client when done
45
client.close()
46
```
47
48
### Ning WS Request
49
50
Ning-specific WSRequest implementation with additional methods.
51
52
```scala { .api }
53
/**
54
* Ning-specific WSRequest implementation
55
*/
56
case class NingWSRequest(
57
client: NingWSClient,
58
url: String,
59
method: String = "GET",
60
body: WSBody = EmptyBody,
61
headers: Map[String, Seq[String]] = Map.empty,
62
queryString: Map[String, Seq[String]] = Map.empty,
63
calc: Option[WSSignatureCalculator] = None,
64
auth: Option[(String, String, WSAuthScheme)] = None,
65
followRedirects: Option[Boolean] = None,
66
requestTimeout: Option[Int] = None,
67
virtualHost: Option[String] = None,
68
proxyServer: Option[WSProxyServer] = None
69
) extends WSRequest {
70
71
// Additional Ning-specific methods
72
/** Get current request headers */
73
def requestHeaders: Map[String, Seq[String]]
74
75
/** Get specific request header */
76
def requestHeader(name: String): Option[String]
77
78
/** Get current query parameters */
79
def requestQueryParams: Map[String, Seq[String]]
80
81
/** Get current URL (may include signature parameters) */
82
def requestUrl: String
83
84
/** Get request body as byte array */
85
def getBody: Option[Array[Byte]]
86
}
87
```
88
89
### Ning WS Response
90
91
Ning-specific WSResponse implementation.
92
93
```scala { .api }
94
/**
95
* Ning-specific WSResponse implementation
96
* @param ahcResponse Underlying AsyncHttpClient response
97
*/
98
case class NingWSResponse(ahcResponse: AHCResponse) extends WSResponse {
99
def status: Int
100
def statusText: String
101
def header(key: String): Option[String]
102
def allHeaders: Map[String, Seq[String]]
103
def cookies: Seq[WSCookie]
104
def cookie(name: String): Option[WSCookie]
105
def body: String
106
def xml: Elem
107
def json: JsValue
108
def bodyAsBytes: Array[Byte]
109
def underlying[T]: T
110
}
111
```
112
113
### Ning Client Configuration
114
115
Comprehensive configuration options for the Ning AsyncHttpClient.
116
117
```scala { .api }
118
/**
119
* Ning WSClient configuration
120
*/
121
case class NingWSClientConfig(
122
wsClientConfig: WSClientConfig = WSClientConfig(),
123
allowPoolingConnection: Boolean = true,
124
allowSslConnectionPool: Boolean = true,
125
ioThreadMultiplier: Int = 2,
126
maxConnectionsPerHost: Int = -1,
127
maxConnectionsTotal: Int = -1,
128
maxConnectionLifetime: Duration = Duration.Inf,
129
idleConnectionInPoolTimeout: Duration = 1.minute,
130
webSocketIdleTimeout: Duration = 15.minutes,
131
maxNumberOfRedirects: Int = 5,
132
maxRequestRetry: Int = 5,
133
disableUrlEncoding: Boolean = false
134
)
135
```
136
137
**Advanced Ning Configuration:**
138
139
```scala
140
import scala.concurrent.duration._
141
142
val ningConfig = NingWSClientConfig(
143
wsClientConfig = WSClientConfig(
144
connectionTimeout = 10.seconds,
145
requestTimeout = 30.seconds,
146
followRedirects = true
147
),
148
allowPoolingConnection = true,
149
maxConnectionsPerHost = 20,
150
maxConnectionsTotal = 100,
151
maxConnectionLifetime = 5.minutes,
152
idleConnectionInPoolTimeout = 2.minutes,
153
maxNumberOfRedirects = 3,
154
maxRequestRetry = 2
155
)
156
157
val client = NingWSClient(ningConfig)
158
```
159
160
### Ning Configuration Factory
161
162
Factory for creating Ning configurations from WS configurations.
163
164
```scala { .api }
165
/**
166
* Factory for creating Ning configurations
167
*/
168
object NingWSClientConfigFactory {
169
/**
170
* Create Ning configuration from WSClient configuration
171
*/
172
def forClientConfig(config: WSClientConfig): NingWSClientConfig
173
}
174
```
175
176
### AsyncHttpClient Configuration Builder
177
178
Builder for creating AsyncHttpClient configurations.
179
180
```scala { .api }
181
/**
182
* Builder for AsyncHttpClient configuration
183
*/
184
class NingAsyncHttpClientConfigBuilder(ningConfig: NingWSClientConfig) {
185
/** Configure the AsyncHttpClient builder */
186
def configure(): AsyncHttpClientConfig.Builder
187
188
/** Build the final configuration */
189
def build(): AsyncHttpClientConfig
190
191
/** Modify the underlying builder */
192
def modifyUnderlying(
193
modify: AsyncHttpClientConfig.Builder => AsyncHttpClientConfig.Builder
194
): NingAsyncHttpClientConfigBuilder
195
}
196
```
197
198
**Custom AsyncHttpClient Configuration:**
199
200
```scala
201
import com.ning.http.client.AsyncHttpClientConfig
202
203
val builder = new NingAsyncHttpClientConfigBuilder(ningConfig)
204
.modifyUnderlying { ahcBuilder =>
205
ahcBuilder
206
.setUserAgent("MyApp/1.0")
207
.setCompressionEnforced(true)
208
.setFollowRedirect(true)
209
}
210
211
val ahcConfig = builder.build()
212
val client = NingWSClient(ahcConfig)
213
```
214
215
### Dependency Injection Support
216
217
Play Framework dependency injection support for Ning WS client.
218
219
```scala { .api }
220
/**
221
* Play module for Ning WS dependency injection
222
*/
223
class NingWSModule extends Module {
224
def bindings(environment: Environment, configuration: Configuration): Seq[Binding[_]]
225
}
226
227
/**
228
* Injectable WSAPI implementation using Ning
229
*/
230
class NingWSAPI @Inject()(client: WSClient) extends WSAPI {
231
def client: WSClient
232
def url(url: String): WSRequest
233
}
234
235
/**
236
* Components trait for manual dependency injection
237
*/
238
trait NingWSComponents {
239
def wsClient: WSClient
240
def wsApi: WSAPI
241
}
242
```
243
244
**Manual Dependency Injection Setup:**
245
246
```scala
247
import play.api.libs.ws.ning._
248
249
trait MyApplicationComponents extends NingWSComponents {
250
// Provide WSClient implementation
251
lazy val wsClient: WSClient = {
252
val config = NingWSClientConfig()
253
NingWSClient(config)
254
}
255
256
// Provide WSAPI implementation
257
lazy val wsApi: WSAPI = new NingWSAPI(wsClient)
258
259
// Don't forget to close client on app shutdown
260
override def applicationLifecycle: ApplicationLifecycle = {
261
val lifecycle = super.applicationLifecycle
262
lifecycle.addStopHook(() => Future.successful(wsClient.close()))
263
lifecycle
264
}
265
}
266
```
267
268
### Connection Pool Management
269
270
Managing connection pools with Ning client.
271
272
```scala
273
// Configuration for connection pooling
274
val poolConfig = NingWSClientConfig(
275
allowPoolingConnection = true,
276
allowSslConnectionPool = true,
277
maxConnectionsPerHost = 50, // Max connections per host
278
maxConnectionsTotal = 200, // Max total connections
279
maxConnectionLifetime = 10.minutes, // Max connection age
280
idleConnectionInPoolTimeout = 5.minutes // Idle timeout
281
)
282
283
val client = NingWSClient(poolConfig)
284
285
// The client will automatically manage the connection pool
286
// Connections are reused for better performance
287
val response1 = client.url("https://api.example.com/endpoint1").get()
288
val response2 = client.url("https://api.example.com/endpoint2").get()
289
290
// Both requests can reuse the same connection to api.example.com
291
```
292
293
### Request Retry Configuration
294
295
Configure automatic request retries for failed requests.
296
297
```scala
298
val retryConfig = NingWSClientConfig(
299
maxRequestRetry = 3, // Retry failed requests up to 3 times
300
maxNumberOfRedirects = 5 // Follow up to 5 redirects
301
)
302
303
val client = NingWSClient(retryConfig)
304
305
// This request will be automatically retried on failure
306
val response = client.url("https://unreliable-api.com/data").get()
307
```
308
309
### WebSocket Configuration
310
311
Configure WebSocket idle timeout (for when using WS client with WebSockets).
312
313
```scala
314
val wsConfig = NingWSClientConfig(
315
webSocketIdleTimeout = 30.minutes // Close idle WebSocket connections after 30 minutes
316
)
317
318
val client = NingWSClient(wsConfig)
319
```
320
321
### Performance Tuning
322
323
Optimize Ning client for different scenarios.
324
325
**High Throughput Configuration:**
326
327
```scala
328
val highThroughputConfig = NingWSClientConfig(
329
wsClientConfig = WSClientConfig(
330
connectionTimeout = 5.seconds,
331
requestTimeout = 30.seconds
332
),
333
allowPoolingConnection = true,
334
allowSslConnectionPool = true,
335
ioThreadMultiplier = 4, // More I/O threads for high concurrency
336
maxConnectionsPerHost = 100,
337
maxConnectionsTotal = 500,
338
maxConnectionLifetime = 30.minutes,
339
idleConnectionInPoolTimeout = 10.minutes
340
)
341
```
342
343
**Low Latency Configuration:**
344
345
```scala
346
val lowLatencyConfig = NingWSClientConfig(
347
wsClientConfig = WSClientConfig(
348
connectionTimeout = 1.second,
349
requestTimeout = 5.seconds
350
),
351
allowPoolingConnection = true,
352
maxConnectionsPerHost = 10, // Smaller pool for faster connection reuse
353
maxConnectionsTotal = 50,
354
idleConnectionInPoolTimeout = 1.minute, // Shorter idle timeout
355
maxRequestRetry = 1 // Fewer retries for lower latency
356
)
357
```
358
359
**Resource-Constrained Configuration:**
360
361
```scala
362
val resourceConstrainedConfig = NingWSClientConfig(
363
allowPoolingConnection = true,
364
maxConnectionsPerHost = 5, // Limit connections
365
maxConnectionsTotal = 20, // Lower total pool size
366
maxConnectionLifetime = 2.minutes, // Shorter connection lifetime
367
idleConnectionInPoolTimeout = 30.seconds, // Quick idle cleanup
368
ioThreadMultiplier = 1 // Fewer threads
369
)
370
```
371
372
### Custom Request/Response Processing
373
374
Access underlying Ning objects for advanced processing.
375
376
```scala
377
client.url("https://api.example.com/data").get().map { response =>
378
// Access underlying Ning response
379
val ningResponse = response.underlying[com.ning.http.client.Response]
380
381
// Get additional response information
382
val responseTime = ningResponse.getResponseTime
383
val uri = ningResponse.getUri
384
val contentLength = ningResponse.getContentLength
385
386
println(s"Response time: ${responseTime}ms")
387
println(s"Content length: $contentLength bytes")
388
}
389
```
390
391
### Complete Example: Production Ning Setup
392
393
```scala
394
import play.api.libs.ws.ning._
395
import play.api.libs.ws.ssl._
396
import scala.concurrent.duration._
397
import scala.concurrent.ExecutionContext.Implicits.global
398
399
// Create production-ready Ning configuration
400
val productionConfig = NingWSClientConfig(
401
wsClientConfig = WSClientConfig(
402
connectionTimeout = 10.seconds,
403
idleTimeout = 60.seconds,
404
requestTimeout = 30.seconds,
405
followRedirects = true,
406
useProxyProperties = true,
407
userAgent = Some("MyApp/2.0"),
408
compressionEnabled = true,
409
ssl = SSLConfig(
410
protocol = "TLSv1.2",
411
enabledProtocols = Some(Seq("TLSv1.2"))
412
)
413
),
414
allowPoolingConnection = true,
415
allowSslConnectionPool = true,
416
ioThreadMultiplier = 2,
417
maxConnectionsPerHost = 20,
418
maxConnectionsTotal = 100,
419
maxConnectionLifetime = 10.minutes,
420
idleConnectionInPoolTimeout = 5.minutes,
421
maxNumberOfRedirects = 3,
422
maxRequestRetry = 2,
423
disableUrlEncoding = false
424
)
425
426
// Create and use the client
427
val client = NingWSClient(productionConfig)
428
429
// Make requests
430
val future = for {
431
response1 <- client.url("https://api.service1.com/data").get()
432
response2 <- client.url("https://api.service2.com/data").get()
433
} yield {
434
(response1.json, response2.json)
435
}
436
437
future.map { case (json1, json2) =>
438
// Process responses
439
println("Data retrieved successfully")
440
}.recover { case ex =>
441
println(s"Request failed: ${ex.getMessage}")
442
}.andThen { case _ =>
443
// Always clean up resources
444
client.close()
445
}
446
```