aboutsummaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
authoradamw <adam@warski.org>2017-07-16 10:51:47 +0200
committeradamw <adam@warski.org>2017-07-16 10:51:47 +0200
commitd0cbe4cf6385b0a896e504d8e66e8a36221a42d7 (patch)
treed0be2a0aa5e980b0b5aabf1a40ca307ef1deda3b /README.md
parent93ed3fafeb90b589933ffaeee928b95ca099b35a (diff)
downloadsttp-d0cbe4cf6385b0a896e504d8e66e8a36221a42d7.tar.gz
sttp-d0cbe4cf6385b0a896e504d8e66e8a36221a42d7.tar.bz2
sttp-d0cbe4cf6385b0a896e504d8e66e8a36221a42d7.zip
README
Diffstat (limited to 'README.md')
-rw-r--r--README.md218
1 files changed, 182 insertions, 36 deletions
diff --git a/README.md b/README.md
index d540063..2889256 100644
--- a/README.md
+++ b/README.md
@@ -5,56 +5,114 @@
The HTTP client for Scala that you always wanted
```scala
-val user = "adamw"
-val state = "closed"
+import com.softwaremill.sttp._
+
val sort: Option[String] = None
-val request = sttp.get(uri"https://api.github.com/repos/$user/elasticmq/issues?state=$state&sort=$sort")
+val query = "http language:scala"
+
+// the `query` parameter is automatically url-encoded and `sort` removed
+val request = sttp.get(uri"https://api.github.com/search/repositories?q=$query&sort=$sort")
+// response body is read into a string, no need to remember to consume it later
val response = request.send(responseAsString("utf-8"))
-println(response.header("Content-Length")) // has type Option[String]
-println(response.body) // has type String as specified when sending the request
+// response.header(...): Option[String]
+println(response.header("Content-Length"))
+
+// response.body: String as specified when sending the request
+println(response.body)
```
## Goals of the project
-* provide a simple, discoverable, no-surprises, reasonably type-safe API for making HTTP requests
+* provide a simple, discoverable, no-surprises, reasonably type-safe API for
+making HTTP requests and reading responses
* separate definition of a request from request execution
-* provide immutable, easily modifiable data structures for requests and responses
-* support both synchronous and asynchronous execution backends
+* provide immutable, easily modifiable data structures for requests and
+responses
+* support multiple execution backends, both synchronous and asynchronous
* provide support for backend-specific request/response streaming
+* minimum dependencies
## How is sttp different from other libraries?
-* immutable request builder which doesn't impose any order in which request parameters need to be specified.
-One consequence of that approach is that the URI doesn't need to be specified upfront. Allows defining partial requests
-which contain common cookies/headers/options, which can later be specialized using a specific URI and HTTP method.
-* support for multiple backends, both synchronous and asynchronous, with backend-specific streaming support
-* URI interpolator with optional parameters support
+* immutable request builder which doesn't impose any order in which request
+parameters need to be specified. Such an approach allows defining partial
+requests with common cookies/headers/options, which can later be specialized
+using a specific URI and HTTP method.
+* support for multiple backends, both synchronous and asynchronous, with
+backend-specific streaming support
+* URI interpolator with context-aware escaping, optional parameters support
+and parameter collections
+
+## Adding sttp to your project
+
+SBT dependency:
+
+```scala
+"com.softwaremill.sttp" %% "core" % version
+```
+
+Check the maven badge above or git tags for the latest version. `sttp` is
+available for Scala 2.11 and 2.12, and requires Java 8. The core module has
+no transitive dependencies.
+
+If you'd like to use an alternate backend, [see below](#supported-backends)
+for additional instructions.
-## Usage
+## API
-First, make sure to import:
+First, import:
```scala
import com.softwaremill.sttp._
```
-To send requests, you will also need a backend. A default, synchronous backend based on Java's `HttpURLConnection`
-is provided out-of-the box. An implicit value needs to be in scope to invoke `send()` (however it's possible to
-create request descriptions without any implicit backend in scope):
+This brings into scope `sttp`, the empty request, from which all request
+definitions start. This empty request can be customised, each time yielding
+a new, immutable request description (unless a mutable body is set on the
+request, such as a byte array).
+
+For example, we can set a cookie, string-body and specify that this should
+be a `POST` request to a given URI:
+
+```scala
+val request = sttp
+ .cookie("login", "me")
+ .body("This is a test")
+ .post(uri"http://endpoint.com/secret")
+```
+
+The request parameters (headers, cookies, body etc.) can be specified in any
+order. There's a lot of ways in which you can customize a request: just
+explore the API. And [more will be added](#todo)!
+
+You can create a request description without knowing how it will be sent.
+But to send a request, you will need a backend. A default, synchronous backend
+based on Java's `HttpURLConnection` is provided out-of-the box. An implicit
+value of type `SttpHandler` needs to be in scope to invoke the `send()` on the
+request:
```scala
implicit val handler = HttpConnectionSttpHandler
+
+val response: Response[String] = request.send(responseAsString)
```
-Any request definition starts from `sttp`: the empty request. This can be further customised, each time yielding a new,
-immutable request description (unless a mutable body is set on the request, such as a byte array).
+Note that when sending the request, we have to specify how to read the response
+body. That way, you don't need to remember to consume it later, avoiding a
+potential resource leak. Response bodies can be ignored (`ignoreResponseBody`),
+read into a parameter sequence (`responseAsParams`) and more; some backends
+also support request & response streaming.
+
+The default handler doesn't wrap the response into any container, but other
+asynchronous handlers might do so. The type parameter in the `Response[_]`
+type specifies the type of the body.
## URI interpolator
-Using the URI interpolator it's possible to conveniently create `java.net.URI` instances, which can then be used
-to specify request endpoints, for example:
+Using the URI interpolator it's possible to conveniently create `java.net.URI`
+instances, which can then be used to specify request endpoints, for example:
```scala
import com.softwaremill.sttp._
@@ -66,19 +124,24 @@ val filter = "programming languages"
val endpoint: URI = uri"http://example.com/$user/skills?filter=$filter"
```
-Any values embedded in the URI will be URL-encoded, taking into account the context (e.g., the whitespace in `user` will
-be %-encoded as `%20D`, while the whitespace in `filter` will be query-encoded as `+`).
+Any values embedded in the URI will be URL-encoded, taking into account the
+context (e.g., the whitespace in `user` will be %-encoded as `%20D`, while the
+whitespace in `filter` will be query-encoded as `+`).
The possibilities of the interpolator don't end here. Other supported features:
-* parameters can have optional values: if the value of a parameter is `None`, it will be removed
-* maps, sequences of tuples and sequences of values can be embedded in the query part. They will be expanded into
-query parameters. Maps and sequences of tuples can also contain optional values, for which mappings will be removed
+* parameters can have optional values: if the value of a parameter is `None`,
+it will be removed
+* maps, sequences of tuples and sequences of values can be embedded in the query
+part. They will be expanded into query parameters. Maps and sequences of tuples
+can also contain optional values, for which mappings will be removed
if `None`.
-* optional values in the host part will be expanded to a subdomain if `Some`, removed if `None`
+* optional values in the host part will be expanded to a subdomain if `Some`,
+removed if `None`
* sequences in the host part will be expanded to a subdomain sequence
-* if a string contains the protocol is embedded *as the first element*, it will not be escaped, allowing to specialize
-entire addresses, e.g.: `uri"$endpoint/login"`, where `val endpoint = "http://example.com/api"`.
+* if a string contains the protocol is embedded *as the first element*, it will
+not be escaped, allowing to embed entire addresses as prefixes, e.g.:
+`uri"$endpoint/login"`, where `val endpoint = "http://example.com/api"`.
A fully-featured example:
@@ -96,15 +159,88 @@ uri"$scheme://$subdomains.example.com?x=$vx&$params#$jumpTo"
// https://sub1.sub2.example.com?x=y+z&a=1&b=2#section2
```
+## Supported backends
+
+### `HttpURLConnectionSttpHandler`
+
+The default **synchronous** handler. Sending a request returns a response wrapped
+in the identity type constructor, which is equivalent to no wrapper at all.
+
+To use, add an implicit value:
+
+```scala
+implicit val sttpHandler = HttpURLConnectionSttpHandler
+```
+
+### `AkkaHttpSttpHandler`
+
+To use, add the following dependency to your project:
+
+```scala
+"com.softwaremill.sttp" %% "akka-http-handler" % version
+```
+
+This handler depends on [akka-http](http://doc.akka.io/docs/akka-http/current/scala/http/).
+A fully **asynchronous** handler. Sending a request returns a response wrapped
+in a `Future`.
+
+To use, add an implicit value:
+
+```scala
+implicit val sttpHandler = new AkkaHttpSttpHandler()
+
+// or, if you'd like to use an existing actor system:
+implicit val sttpHandler = new AkkaHttpSttpHandler(actorSystem)
+```
+
+This backend supports sending and receiving
+[akka-streams](http://doc.akka.io/docs/akka/current/scala/stream/index.html)
+streams of type `akka.stream.scaladsl.Source[ByteString, Any]`.
+
+To set the request body as a stream:
+
+```scala
+import com.softwaremill.sttp._
+import com.softwaremill.sttp.akkahttp._
+
+import akka.stream.scaladsl.Source
+import akka.util.ByteString
+
+val source: Source[ByteString, Any] = ...
+
+sttp
+ .body(source)
+ .post(uri"...")
+```
+
+To receive the response body as a stream:
+
+```scala
+import com.softwaremill.sttp._
+
+import akka.stream.scaladsl.Source
+import akka.util.ByteString
+
+implicit val sttpHandler = new AkkaHttpSttpHandler(actorSystem)
+
+val response: Future[Response[Source[ByteString, Any]]] =
+ sttp
+ .post(uri"...")
+ .send(responseAsStream[Source[ByteString, Any]])
+```
+
## Request types
-All requests have type `RequestTemplate[U]`, where `U[_]` specifies if the request method and URL are specified. There
-are two type aliases for the request template that are used:
+All requests have type `RequestTemplate[U]`, where `U[_]` specifies if the
+request method and URL are specified. There are two type aliases for the
+request template that are used:
-* `type Request = RequestTemplate[Id]`, where `type Id[X] = X` is the identity, meaning that the request has both a
-method and URI.
-* `type PartialRequest = RequestTemplate[Empty]`, where `type Empty[X] = None`, meaning that the request has neither
-a method nor an URI. Both of these fields will be set to `None` (the `Option` subtype).
+* `type Request = RequestTemplate[Id]`, where `type Id[X] = X` is the identity,
+meaning that the request has both a method and an URI specified. Such a request
+can be sent.
+* `type PartialRequest = RequestTemplate[Empty]`, where `type Empty[X] = None`,
+meaning that the request has neither a method nor an URI. Both of these fields
+will be set to `None` (the `Option` subtype). Such a request cannot be sent.
## Notes
@@ -115,6 +251,16 @@ a method nor an URI. Both of these fields will be set to `None` (the `Option` su
* `multipart/form-data` for multipart form data
* `application/octet-stream` for everything else (binary)
+## TODO
+
+* multi-part uploads
+* netty-based backend
+* backends which wrap the responses in scalaz/monix/... `Task`
+* proxy support
+* compression support
+* connection options, SSL
+* *your API improvement idea here*
+
## Other Scala HTTP clients
* [scalaj](https://github.com/scalaj/scalaj-http)