aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authoradamw <adam@warski.org>2017-10-17 17:28:50 +0200
committeradamw <adam@warski.org>2017-10-17 17:28:50 +0200
commit6e109a964383bfe5e2be04f65fa7cc1356a97cbe (patch)
tree7764ff94d72e0ffbf1e593fb8c5886562dc57f33 /docs
parent06bd5c95d04dd57e1b6c2572b94336b8fdb68bfa (diff)
downloadsttp-6e109a964383bfe5e2be04f65fa7cc1356a97cbe.tar.gz
sttp-6e109a964383bfe5e2be04f65fa7cc1356a97cbe.tar.bz2
sttp-6e109a964383bfe5e2be04f65fa7cc1356a97cbe.zip
More docs
Diffstat (limited to 'docs')
-rw-r--r--docs/backends/akkahttp.rst10
-rw-r--r--docs/backends/asynchttpclient.rst2
-rw-r--r--docs/backends/custom.rst14
-rw-r--r--docs/backends/httpurlconnection.rst3
-rw-r--r--docs/backends/start_stop.rst14
-rw-r--r--docs/backends/summary.rst13
-rw-r--r--docs/backends/tagging.rst9
-rw-r--r--docs/backends/testing.rst (renamed from docs/testing.rst)0
-rw-r--r--docs/community.rst8
-rw-r--r--docs/conf/redirects.rst10
-rw-r--r--docs/goals.rst4
-rw-r--r--docs/index.rst77
-rw-r--r--docs/json.rst4
-rw-r--r--docs/quickstart.rst37
-rw-r--r--docs/requests/authentication.rst13
-rw-r--r--docs/requests/basics.rst52
-rw-r--r--docs/requests/body.rst81
-rw-r--r--docs/requests/cookies.rst28
-rw-r--r--docs/requests/defaults.rst11
-rw-r--r--docs/requests/headers.rst30
-rw-r--r--docs/requests/multipart.rst42
-rw-r--r--docs/requests/streaming.rst24
-rw-r--r--docs/requests/uri.rst71
-rw-r--r--docs/responses/basics.rst50
-rw-r--r--docs/responses/body.rst86
-rwxr-xr-xdocs/watch.sh2
26 files changed, 581 insertions, 114 deletions
diff --git a/docs/backends/akkahttp.rst b/docs/backends/akkahttp.rst
index 41e3a7c..53ebfda 100644
--- a/docs/backends/akkahttp.rst
+++ b/docs/backends/akkahttp.rst
@@ -1,3 +1,5 @@
+.. _akkahttp:
+
``AkkaHttpBackend``
===================
@@ -5,9 +7,7 @@ To use, add the following dependency to your project::
"com.softwaremill.sttp" %% "akka-http-backend" % "0.0.20"
-This backend depends on `akka-http <http://doc.akka.io/docs/akka-http/current/scala/http/>`_.
-A fully **asynchronous** backend. Sending a request returns a response wrapped
-in a ``Future``.
+This backend depends on `akka-http <http://doc.akka.io/docs/akka-http/current/scala/http/>`_. A fully **asynchronous** backend. Sending a request returns a response wrapped in a ``Future``.
Next you'll need to add an implicit value::
@@ -16,9 +16,7 @@ Next you'll need to add an implicit value::
// or, if you'd like to use an existing actor system:
implicit val sttpBackend = AkkaHttpBackend.usingActorSystem(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]``.
+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::
diff --git a/docs/backends/asynchttpclient.rst b/docs/backends/asynchttpclient.rst
index cd359b1..8408175 100644
--- a/docs/backends/asynchttpclient.rst
+++ b/docs/backends/asynchttpclient.rst
@@ -19,7 +19,7 @@ The responses are wrapped depending on the dependency chosen in either a:
* standard Scala ``Future``
* `Scalaz <https://github.com/scalaz/scalaz>`_ ``Task``. There's a transitive dependency on ``scalaz-concurrent``.
-* `Monix <https://monix.io`_ ``Task``. There's a transitive dependency on ``monix-eval``.
+* `Monix <https://monix.io>`_ ``Task``. There's a transitive dependency on ``monix-eval``.
* Any type implementing the `Cats Effect <https://github.com/typelevel/cats-effect>`_ ``Async`` typeclass, such as ``cats.effect.IO``. There's a transitive dependency on ``cats-effect``.
Next you'll need to add an implicit value::
diff --git a/docs/backends/custom.rst b/docs/backends/custom.rst
index 86d750a..4dd5208 100644
--- a/docs/backends/custom.rst
+++ b/docs/backends/custom.rst
@@ -1,11 +1,9 @@
+.. _custombackends:
+
Custom backends, logging, metrics
=================================
-It is also entirely possible to write your own backend (if so, please consider
-contributing!) or wrapping an existing one. You can even write completely
-generic wrappers for any delegate backend, as each backend comes equipped
-with a monad for the response type. This brings the possibility to ``map`` and
-``flatMap`` over responses.
+It is also entirely possible to write custom own backend (if doing so, please consider contributing!) or wrapping an existing one. One can even write completely generic wrappers for any delegate backend, as each backend comes equipped with a monad for the response type. This brings the possibility to ``map`` and ``flatMap`` over responses.
Possible use-cases for wrapper-backend include:
@@ -13,7 +11,7 @@ Possible use-cases for wrapper-backend include:
* capturing metrics
* request signing (transforming the request before sending it to the delegate)
-To pass some context to wrapper-backends, requests can be *tagged*. Each
-``RequestT`` instance contains a ``tags: Map[String, Any]`` field. This is unused
-by http, but can be used e.g. to pass a metric name or logging context.
+To pass some context to wrapper-backends, requests can be *tagged*. Each ``RequestT`` instance contains a ``tags: Map[String, Any]`` field. This is unused by http, but can be used e.g. to pass a metric name or logging context.
+
+Example backend logging
diff --git a/docs/backends/httpurlconnection.rst b/docs/backends/httpurlconnection.rst
index a196343..fdea9b0 100644
--- a/docs/backends/httpurlconnection.rst
+++ b/docs/backends/httpurlconnection.rst
@@ -1,8 +1,7 @@
``HttpURLConnectionBackend``
============================
-The default **synchronous** backend. Sending a request returns a response wrapped
-in the identity type constructor, which is equivalent to no wrapper at all.
+The default **synchronous** backend. 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::
diff --git a/docs/backends/start_stop.rst b/docs/backends/start_stop.rst
index 6cd6f13..ed117a7 100644
--- a/docs/backends/start_stop.rst
+++ b/docs/backends/start_stop.rst
@@ -1,17 +1,9 @@
Starting & cleaning up
======================
-In case of most backends, you should only instantiate a backend once per
-application, as a backend typically allocates resources such as thread or
-connection pools.
+In case of most backends, you should only instantiate a backend once per application, as a backend typically allocates resources such as thread or connection pools.
-When ending the application, make sure to call ``backend.close()``, which will
-free up resources used by the backend (if any). The close process might be
-asynchronous, that is it can complete after the ``close()`` method returns.
+When ending the application, make sure to call ``backend.close()``, which will free up resources used by the backend (if any). The close process might be asynchronous, that is it can complete after the ``close()`` method returns.
-Note that only resources allocated by the backends are freed. For example,
-if you use the ``AkkaHttpBackend()`` the ``close()`` method will terminate the
-underlying actor system. However, if you have provided an existing actor system
-upon backend creation (``AkkaHttpBackend.usingActorSystem``), the ``close()``
-method will be a no-op.
+Note that only resources allocated by the backends are freed. For example, if you use the ``AkkaHttpBackend()`` the ``close()`` method will terminate the underlying actor system. However, if you have provided an existing actor system upon backend creation (``AkkaHttpBackend.usingActorSystem``), the ``close()`` method will be a no-op.
diff --git a/docs/backends/summary.rst b/docs/backends/summary.rst
index 6aa0446..36e50b0 100644
--- a/docs/backends/summary.rst
+++ b/docs/backends/summary.rst
@@ -1,6 +1,19 @@
+.. _backends_summary:
+
Supported backends
==================
+sttp suports a number of synchornous and asynchronous backends. It's the backends that take care of managing connections, sending requests and receiving responses: sttp defines only the API to describe the requests to be send and handle the response data. It's the backends where all the heavy-lifting is done.
+
+Choosing the right backend depends on a number of factors: if you are using sttp to explore some data, or is it a production system; are you using a synchornous, blocking architecture or an asynchronous one; do you work mostly with Scala's ``Future``, or maybe you use some form of a ``Task`` abstraction; finally, if you want to stream requests/responses, or not.
+
+Each backend has two type parameters:
+
+* ``R[_]``, the type constructor in which responses are wrapped. That is, when you invoke ``send()`` on a request description, do you get a ``Response[_]`` directly, or is it wrapped in a ``Future`` or a ``Task``?
+* ``S``, the type of supported streams. If ``Nothing``, streaming is not supported. Otherwise, the given type can be used to send request bodies or receive response bodies.
+
+Below is a summary of all the backends. See the sections on individual backend implementations for more information.
+
================================ ============================ ================================================
Class Result wrapper Supported stream type
================================ ============================ ================================================
diff --git a/docs/backends/tagging.rst b/docs/backends/tagging.rst
new file mode 100644
index 0000000..15773ff
--- /dev/null
+++ b/docs/backends/tagging.rst
@@ -0,0 +1,9 @@
+Request tagging
+===============
+
+Each request contains a ``tags: Map[String, Any]`` map. This map can be used to tag the request with any backend-specific information, and isn't used in any way by sttp itself.
+
+Tags can be added to a request using the ``def tag(k: String, v: Any)`` method, and read using the ``def tag(k: String): Option[Any]`` method.
+
+Backends, or :ref:`backend wrappers <custombackends>` can use tags e.g. for logging, passing a metric name, using different connection pools, or even different delegate backends.
+
diff --git a/docs/testing.rst b/docs/backends/testing.rst
index 0ac21c2..0ac21c2 100644
--- a/docs/testing.rst
+++ b/docs/backends/testing.rst
diff --git a/docs/community.rst b/docs/community.rst
new file mode 100644
index 0000000..4a400e0
--- /dev/null
+++ b/docs/community.rst
@@ -0,0 +1,8 @@
+Community
+=========
+
+If you have a question, or hit a problem, feel free to ask on our `gitter channel <https://gitter.im/softwaremill/sttp>`_!
+
+Or, if you hit a bug, something is unclear in the code or documentation, don't hesitate and `open an issue <https://github.com/softwaremill/sttp/issues>`_ on GitHub.
+
+We are also always looking for contributions and new ideas, so if you'd like to get into the project, check out the open issues, or post your own suggestions!
diff --git a/docs/conf/redirects.rst b/docs/conf/redirects.rst
new file mode 100644
index 0000000..e826c7c
--- /dev/null
+++ b/docs/conf/redirects.rst
@@ -0,0 +1,10 @@
+Redirects
+=========
+
+By default, sttp follows redirects.
+
+If you'd like to disable following redirects, use the ``followRedirects`` method::
+
+ sttp.followRedirects(false)
+
+If a request has been redirected, the history of all followed redirects is accessible through the ``response.history`` list. The first response (oldest) comes first. The body of each response will be a ``Left(message)`` (as the status code is non-2xx), where the message is whatever the server returned as the response body.
diff --git a/docs/goals.rst b/docs/goals.rst
index 9b59991..20f9537 100644
--- a/docs/goals.rst
+++ b/docs/goals.rst
@@ -8,9 +8,7 @@ Goals of the project
* provide support for backend-specific request/response streaming
* minimum dependencies
-See also the `introduction to sttp <https://softwaremill.com/introducing-sttp-the-scala-http-client>`_
-and `sttp streaming & URI interpolators <https://softwaremill.com/sttp-streaming-uri-interpolator>`_
-blogs.
+See also the `introduction to sttp <https://softwaremill.com/introducing-sttp-the-scala-http-client>`_ and `sttp streaming & URI interpolators <https://softwaremill.com/sttp-streaming-uri-interpolator>`_ blogs.
Non-goals of the project
------------------------
diff --git a/docs/index.rst b/docs/index.rst
index 0550cd7..d569a11 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,12 +1,11 @@
sttp: the Scala HTTP client you always wanted!
==============================================
-`sttp <https://github.com/softwaremill/sttp>`_ is an open-source library which provides a clean, programmer-friendly API to define HTTP requests and execute them using one of the wrapped backends, such as `akka-http <https://doc.akka.io/docs/akka-http/current/scala/http/>`_, `async-http-client <https://github.com/AsyncHttpClient/async-http-client>`_ or `OkHttp <http://square.github.io/okhttp/>`_.
+Welcome!
-First impressions
------------------
+`sttp <https://github.com/softwaremill/sttp>`_ is an open-source library which provides a clean, programmer-friendly API to define HTTP requests and execute them using one of the wrapped backends, such as `akka-http <https://doc.akka.io/docs/akka-http/current/scala/http/>`_, `async-http-client <https://github.com/AsyncHttpClient/async-http-client>`_ or `OkHttp <http://square.github.io/okhttp/>`_.
-.. code-block:: scala
+Here's a very quick example of sttp in action::
import com.softwaremill.sttp._
@@ -25,38 +24,42 @@ First impressions
// response.unsafeBody: by default read into a String
println(response.unsafeBody)
-
-
-Quickstart with Ammonite
-------------------------
-
-If you are an `Ammonite <http://ammonite.io>`_ user, you can quickly start
-experimenting with sttp by copy-pasting the following::
-
- import $ivy.`com.softwaremill.sttp::core:0.0.20`
- import com.softwaremill.sttp._
- implicit val backend = HttpURLConnectionBackend()
- sttp.get(uri"http://httpbin.org/ip").send()
-
-Adding sttp to your project
----------------------------
-The basic dependency which provides the default, synchornous backend is::
+For more details, explore the subjects below!
- "com.softwaremill.sttp" %% "core" % "0.0.20"
-
-``sttp`` is available for Scala 2.11 and 2.12, and requires Java 7 if using an ``OkHttp`` based backend, or Java 8 otherwise. The core module has no transitive dependencies.
+.. toctree::
+ :maxdepth: 2
+ :caption: Getting started
-If you'd like to use an alternate backend, [see below](#supported-backends) for additional instructions.
+ quickstart
+ goals
+ community
.. toctree::
:maxdepth: 2
+ :caption: Request definition
- goals
requests/basics
requests/uri
- requests/defaults
+ requests/headers
+ requests/cookies
+ requests/authentication
+ requests/body
+ requests/multipart
+ requests/streaming
requests/type
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Responses
+
+ responses/basics
+ responses/body
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Backends
+
backends/summary
backends/start_stop
backends/httpurlconnection
@@ -64,17 +67,23 @@ If you'd like to use an alternate backend, [see below](#supported-backends) for
backends/asynchttpclient
backends/okhttp
backends/custom
+ backends/testing
+ backends/tagging
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Configuration
+
conf/timeouts
conf/ssl
- conf/proxy
+ conf/proxy
+ conf/redirects
+
+.. toctree::
+ :maxdepth: 2
+ :caption: More information
+
json
- testing
other
credits
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
diff --git a/docs/json.rst b/docs/json.rst
index 28cee81..086fa60 100644
--- a/docs/json.rst
+++ b/docs/json.rst
@@ -1,3 +1,5 @@
+.. _json:
+
JSON
====
@@ -8,7 +10,7 @@ add the following dependency to your project::
"com.softwaremill.sttp" %% "circe" % "0.0.20"
This module adds a method to the request and a function that can be given to
-a request to decode the response to a specific object.
+a request to decode the response to a specific object::
import com.softwaremill.sttp._
import com.softwaremill.sttp.circe._
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
new file mode 100644
index 0000000..4f04fdf
--- /dev/null
+++ b/docs/quickstart.rst
@@ -0,0 +1,37 @@
+.. _quickstart:
+
+Quickstart
+==========
+
+The main sttp API comes in a single jar without transitive dependencies. This also includes a default, synchronous backend, which is based on java's ``HttpURLConnection``. For production usages, you'll often want to use an alternate backend (but what's important, is that the API remains the same!). See the section on :ref:`backends <backends_summary>` for additional instructions.
+
+Using sbt
+---------
+
+The basic dependency which provides the API and the default synchronous backend is::
+
+ "com.softwaremill.sttp" %% "core" % "0.0.20"
+
+``sttp`` is available for Scala 2.11 and 2.12, and requires Java 8. The core module has no transitive dependencies.
+
+Using Ammonite
+--------------
+
+If you are an `Ammonite <http://ammonite.io>`_ user, you can quickly start experimenting with sttp by copy-pasting the following::
+
+ import $ivy.`com.softwaremill.sttp::core:0.0.20`
+ import com.softwaremill.sttp._
+ implicit val backend = HttpURLConnectionBackend()
+ sttp.get(uri"http://httpbin.org/ip").send()
+
+Imports
+-------
+
+Working with sttp is most convenient if you import the ``sttp`` package::
+
+ import com.softwaremill.sttp._
+
+This brings into scope the starting point for defining requests and some convenience helper methods. All examples in this guide assume, that this import is in place.
+
+And that's all you need to start using sttp! To start exploring, import the above, type ``sttp.`` and see where your IDE's auto-complete gets you! Or, read on about the :ref:`basics of defining requests <request_basics>`.
+
diff --git a/docs/requests/authentication.rst b/docs/requests/authentication.rst
new file mode 100644
index 0000000..f97f74a
--- /dev/null
+++ b/docs/requests/authentication.rst
@@ -0,0 +1,13 @@
+Authentication
+==============
+
+sttp supports basic and bearer-token based authentication. In both cases, an ``Authorization`` header is added with the appropriate credentials.
+
+Basic authentication, using which the username and password are encoded using Base64, can be added as follows::
+
+ sttp.auth.basic(username, password)
+
+A bearer token can be added using::
+
+ sttp.auth.bearer(token)
+
diff --git a/docs/requests/basics.rst b/docs/requests/basics.rst
index 6a6202c..fc15f36 100644
--- a/docs/requests/basics.rst
+++ b/docs/requests/basics.rst
@@ -1,43 +1,47 @@
-Defining requests: basics
+.. _request_basics:
+
+Request definition basics
=========================
-To get easy access to the request definition API, add the following import::
+As mentioned in the :ref:`quickstart <quickstart>`, the following import will be needed::
import com.softwaremill.sttp._
-This brings into scope ``sttp``, the starting request (it's an empty request
-with the ``Accept-Encoding: gzip, defalte`` header added). This 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).
+This brings into scope ``sttp``, the starting request. This 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). As the request description is immutable, it can be freely stored in values, shared across threads, and customized multiple times.
-For example, we can set a cookie, string-body and specify that this should
-be a ``POST`` request to a given URI::
+For example, we can set a cookie, string-body and specify that this should be a ``POST`` request to a given URI::
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**. It doesn't matter if the request method, the body, the headers or connection options are specified in this sequence or another. This way you can build arbitrary request templates, capturing all that's common among your requests, and customizing as needed. Remember, that each time a modifier is applied to a request, you get a new immutable object.
+
+There's a lot of ways in which you can customize a request, which are covered in this guide. Another option is to just explore the API: most of the methods are self-explanatory and carry scaladocs if needed.
+
+Using the modifiers, each time we get a new request description, but it's just description: a data object; nothing is sent over the network until the ``send()`` method is invoked.
+
+Sending a request
+-----------------
-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.
+A request description can be created without knowing how it will be sent. But to send a request, a backend is needed. A default, synchronous backend based on Java's ``HttpURLConnection`` is provided out-of-the box.
-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 ``SttpBackend`` needs to be in scope to invoke the ``send()`` on the
-request::
+To invoke the ``send()`` method on a request description, an implicit value of type ``SttpBackend`` needs to be in scope::
implicit val backend = HttpURLConnectionBackend()
val response: Response[String] = request.send()
-By default the response body is read into a utf-8 string. How the response body
-is handled is also part of the request description. The body can be ignore
-(``.response(ignore)``), read into a sequence of parameters
-(``.response(asParams)``), mapped (``.mapResponse``) and more; some backends also
-support request & response streaming.
+The default backend doesn't wrap the response into any container, but other asynchronous backends might do so. See the section on :ref:`backends <backends_summary>` for more details.
+
+Starting requests
+-----------------
+
+sttp provides two starting requests:
+
+* ``sttp``, which is an empty request with the ``Accept-Encoding: gzip, deflate`` header added. That's the one that is most commonly used.
+* ``empty``, a completely empty request, with no headers at all.
+
+Both of these requests will by default read the response body into a UTF-8 ``String``. How the response body is handled is also part of the request description. See the section on :ref:`response body specifications <responsebodyspec>` for more details on how to customize that.
-The default backend doesn't wrap the response into any container, but other
-asynchronous backends might do so. The type parameter in the ``Response[_]``
-type specifies the type of the body.
diff --git a/docs/requests/body.rst b/docs/requests/body.rst
new file mode 100644
index 0000000..bae7526
--- /dev/null
+++ b/docs/requests/body.rst
@@ -0,0 +1,81 @@
+Request bodies
+==============
+
+Text data
+---------
+
+In its simplest form, the request's body can be set as a ``String``. By default, this method will:
+
+* use the UTF-8 encoding to convert the string to a byte array
+* if not specified, set ``Content-Type: text/plain``
+* if not specified, set ``Content-Length`` to the number of bytes in the array
+
+A ``String`` body can be set on a request as follows::
+
+ sttp.body("Hello, world!")
+
+It is also possible to use a different character encoding::
+
+ def body(b: String)
+ def body(b: String, encoding: String)
+
+Binary data
+-----------
+
+To set a binary-data body, the following methods are available::
+
+ def body(b: Array[Byte])
+ def body(b: ByteBuffer)
+ def body(b: InputStream)
+
+If not specified, these methods will set the conten type to ``application/octet-stream``. When using a byte array, additionally the content length will be set to the length of the array (unless specified explicitly).
+
+.. note::
+
+ While the object defining a request is immutable, setting a mutable request body will make the whole request definition mutable as well. With ``InputStream``, the request can be sent only once, as input streams can be consumed once.
+
+Uploading files
+---------------
+
+To upload a file, simply set the request body as a ``File``, ``Path``::
+
+ def body(b: File)
+ def body(b: Path)
+
+As with binary body methods, the content type will default to ``application/octet-stream``, and the content length will be set to the lenght of the file (unless specified explicitly).
+
+See also :ref:`multi-part <multipart>` and :ref:`streaming <streaming>` support.
+
+Form data
+---------
+
+If you set the body as a ``Map[String, String]`` or ``Seq[(String, String)]``, it will be encoded as form-data (as if a web form with the given values was submitted). The content type will default to ``application/x-www-form-urlencoded``; content length will also be set if not specified.
+
+By default, the ``UTF-8`` encoding is used, but can be also specified explicitly::
+
+ def body(fs: Map[String, String])
+ def body(fs: Map[String, String], encoding: String)
+ def body(fs: (String, String)*)
+ def body(fs: Seq[(String, String)], encoding: String)
+
+Custom body serializers
+-----------------------
+
+It is also possible to set custom types as request bodies, as long as there's an implicit ``BodySerializer[B]`` value in scope, which is just an alias for a function::
+
+ type BodySerializer[B] = B => BasicRequestBody
+
+A ``BasicRequestBody`` is a wrapper for a ``String``/byte array/input stream body. For example, here's how to write custom serializer for a case class, with serializer-specific default content type::
+
+ case class Person(name: String, surname: String, age: Int)
+
+ // for this example, assuming names/surnames can't contain commas
+ implicit val personSerializer: BodySerializer[Person] = { p: Person =>
+ val serialized = s"${p.name},${p.surname},${p.age}"
+ StringBody(serialized, "UTF-8", Some("application/csv"))
+ }
+
+ sttp.body(Person("mary", "smith", 67))
+
+See the implementations of the ``BasicRequestBody`` trait for more options.
+
diff --git a/docs/requests/cookies.rst b/docs/requests/cookies.rst
new file mode 100644
index 0000000..0645316
--- /dev/null
+++ b/docs/requests/cookies.rst
@@ -0,0 +1,28 @@
+.. _cookies:
+
+Cookies
+=======
+
+Cookies sent in requests are key-value pairs contained in the ``Cookie`` header. They can be set on a request in a couple of ways. The first is using the ``.cookie(name: String, value: String)`` method. This will yield a new request definition which, when sent, will contain the given cookie.
+
+Cookies can also be set using the following methods::
+
+ def cookie(nv: (String, String))
+ def cookie(n: String, v: String)
+ def cookies(nvs: (String, String)*)
+
+Cookies from responses
+----------------------
+
+It is often necessary to copy cookies from a response, e.g. after a login request is sent, and a successful response with the authentication cookie received. Having an object ``response: Response[_]``, cookies on a request can be copied::
+
+ // Method signature
+ def cookies(r: Response[_])
+
+ // Usage
+ sttp.cookies(response)
+
+Or, it's also possible to store only the ``com.softwaremill.sttp.Cookie`` objects (a sequence of which can be obtained from a response), and set the on the request::
+
+ def cookies(cs: Seq[Cookie])
+
diff --git a/docs/requests/defaults.rst b/docs/requests/defaults.rst
deleted file mode 100644
index f393c04..0000000
--- a/docs/requests/defaults.rst
+++ /dev/null
@@ -1,11 +0,0 @@
-Defaults
-========
-
-The encoding for ``String``s defaults to ``utf-8``.
-
-Unless explicitly specified, the ``Content-Type`` defaults to:
-
-* ``text/plain`` for text
-* ``application/x-www-form-urlencoded`` for form data
-* ``multipart/form-data`` for multipart form data
-* ``application/octet-stream`` for everything else (binary)
diff --git a/docs/requests/headers.rst b/docs/requests/headers.rst
new file mode 100644
index 0000000..1135377
--- /dev/null
+++ b/docs/requests/headers.rst
@@ -0,0 +1,30 @@
+Headers
+=======
+
+Arbitrary headers can be set on the request using the ``.header`` method::
+
+ sttp.header("User-Agent", "myapp")
+
+As with any other request definition modifier, this method will yield a new request, which has the given header set. The headers can be set at any point when defining the request, arbitrarily interleaved with other modifiers.
+
+While most headers should be set only once on a request, HTTP allows setting a header multiple times. That's why the ``header`` method has an additional optional boolean parameter, ``replaceExisting``, which defaults to ``true``. This way, if the same header is specified twice, only the last value will be included in the request. If previous values should be preserved, set this parameter to ``false``.
+
+There are also variants of this method accepting a number of headers::
+
+ def header(k: String, v: String, replaceExisting: Boolean = false)
+ def headers(hs: Map[String, String])
+ def headers(hs: (String, String)*)
+
+Both of the ``headers`` append the given headers to the ones currently in the request, without removing duplicates.
+
+Common headers
+--------------
+
+For some common headers, dedicated methods are provided::
+
+ def contentType(ct: String)
+ def contentType(ct: String, encoding: String)
+ def contentLength(l: Long)
+ def acceptEncoding(encoding: String)
+
+See also [cookies], [authentication].
diff --git a/docs/requests/multipart.rst b/docs/requests/multipart.rst
new file mode 100644
index 0000000..5a1d11d
--- /dev/null
+++ b/docs/requests/multipart.rst
@@ -0,0 +1,42 @@
+.. _multipart:
+
+Multipart requests
+==================
+
+To set a multipart body on a request, the ``multipartBody`` method should be used (instead of ``body``). Each body part is represented as an instance of ``Multipart``, which can be conveniently constructed using ``multipart`` methods.
+
+A single part of a multipart request can be of type:
+
+* ``String``
+* ``Array[Byte]``
+* ``ByteBuffer``
+* ``InputStream``
+* ``File``
+* ``Path``
+
+The content type of each part is by default the same as when setting simple bodies: ``text/plain`` for parts of type ``String``, ``application/x-www-form-urlencoded`` for parts of key-value pairs (form data) and ``application/octet-stream`` otherwise (for binary data).
+
+The parts can be specified using either a ``Seq[Multipart]`` or by using multiple arguments::
+
+ def multipartBody(ps: Seq[Multipart])
+ def multipartBody(p1: Multipart, ps: Multipart*)
+
+For example::
+
+ sttp.multipartBody(
+ multipart("text_part", "data1"),
+ multipart("file_part", someFile), // someFile: File
+ multipart("form_part", Map("x" -> "10", "y" -> "yes"))
+ )
+
+Customising part meta-data
+--------------------------
+
+For each part, an optional filename can be specified, as well as a custom content type and additional headers. The following methods are available on ``Multipart``::
+
+ case class Multipart {
+ def fileName(v: String): Multipart
+ def contentType(v: String): Multipart
+ def header(k: String, v: String): Multipart
+ }
+
diff --git a/docs/requests/streaming.rst b/docs/requests/streaming.rst
new file mode 100644
index 0000000..600630c
--- /dev/null
+++ b/docs/requests/streaming.rst
@@ -0,0 +1,24 @@
+.. _streaming:
+
+Streaming
+=========
+
+Some backends (see :ref:`backends summary <backends_summary>`) support streaming bodies. If that's the case, you can set a stream of the supported type as a request body using the ``streamBody`` method.
+
+For example, using the :ref:`akka-http backend <akkahttp>`, a request with a stream body can be defined as follows::
+
+ import com.softwaremill.sttp._
+ import com.softwaremill.sttp.akkahttp._
+
+ import akka.stream.scaladsl.Source
+ import akka.util.ByteString
+
+ val source: Source[ByteString, Any] = ...
+
+ sttp
+ .streamBody(source)
+ .post(uri"...")
+
+.. note::
+
+ A request with the body set as a stream can only be sent using a backend supporting exactly the given type of streams.
diff --git a/docs/requests/uri.rst b/docs/requests/uri.rst
index bcb7c88..17e0a2c 100644
--- a/docs/requests/uri.rst
+++ b/docs/requests/uri.rst
@@ -1,7 +1,16 @@
-Defining requests: URI Interpolator
-===================================
+URIs
+====
-Using the URI interpolator it's possible to conveniently create ``Uri` instances, which can then be used to specify request endpoints, for example::
+A request can only be sent if the request method & URI are defined. To represent URIs, sttp comes with a ``Uri`` case class, which captures all of the parts of an address.
+
+To specify the request method and URI, use one of the methods on the request description corresponding to the name of the method: ``.post``, ``.get``, ``.put`` etc. All of them accept a single parameter, the URI to which the request should be send, and yield a request description which, when sent, will use the given address (these methods only modify the request description; they don't send the requests).
+
+The ``Uri`` class is immutable, and can be constructed by hand, but in many cases the URI interpolator will be useful.
+
+URI interpolator
+----------------
+
+Using the URI interpolator it's possible to conveniently create ``Uri`` instances, for example::
import com.softwaremill.sttp._
@@ -10,18 +19,54 @@ Using the URI interpolator it's possible to conveniently create ``Uri` instances
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 ``+``).
+ assert(endpoint.toString == "http://example.com/Mary%20Smith/skills?filter=programming+languages")
+
+Note the ``uri`` prefix before the string and the standard Scala string-embedding syntax (``$user``, ``$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 ``+``).
+
+All components of the URI can be embedded from values: scheme, username/password, host, port, path, query and fragment.
+
+Optional values
+---------------
+
+The URI interpolator supports optional values for hosts (subdomains), query parameters and the fragment. If the value is ``None``, the appropriate URI component will be removed. For example::
+
+ val v1 = None
+ val v2 = Some("v2")
+
+ val u1 = uri"http://example.com?p1=$v1&p2=v2"
+ assert(u1.toString == "http://example.com?p2=v2")
+
+ val u2 = uri"http://$v1.$v2.example.com"
+ assert(u2.toString == "http://v2.example.com")
+
+ val u3 = uri"http://example.com#$v1"
+ assert(u3.toString == "http://example.com")
+
+Maps and sequences
+------------------
+
+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``.
+
+For example::
+
+ val ps = Map("p1" -> "v1", "p2" -> "v2")
+ val u4 = uri"http://example.com?$ps&p3=p4"
+ assert(u4.toString == "http://example.com?p1=v1&p2=v2&p3=p4")
+
+Sequences in the host part will be expanded to a subdomain sequence.
+
+Special cases
+-------------
+
+If a string containing the protocol is embedded *as the very beginning*, it will not be escaped, allowing to embed entire addresses as prefixes, e.g.: ``uri"$endpoint/login"``, where ``val endpoint = "http://example.com/api"``.
+
+This is useful when a base URI is stored in a value, and can then be used as a base for constructing URIs.
-The possibilities of the interpolator don't end here. Other supported features:
+All features combined
+---------------------
-* 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``
-* sequences in the host part will be expanded to a subdomain sequence
-* if a string containing the protocol is embedded *as the very beginning*, 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::
import com.softwaremill.sttp._
diff --git a/docs/responses/basics.rst b/docs/responses/basics.rst
new file mode 100644
index 0000000..6157647
--- /dev/null
+++ b/docs/responses/basics.rst
@@ -0,0 +1,50 @@
+Responses
+=========
+
+Responses are represented as instances of the case class ``Response[T]``, where ``T`` is the type of the response body. When sending a request, the response will be returned in a wrapper. For example, for asynchronous backends, we will get a ``Future[Response[T]]``, while for the default synchronous backend, the wrapper backend will be the no-op wrapper, ``Id``, which is the same as no wrapper at all.
+
+If sending the request fails, either due to client or connection errors, an exception will be thrown (synchronous backends), or an error will be represented in the wrapper (e.g. a failed future).
+
+.. note::
+
+ If the request completes, but results in a non-2xx return code, the request is still considered successful, that is, a ``Response[T]`` will be returned. See the next subsection for details regarding body handling.
+
+Response code
+-------------
+
+The response code is available through the ``.code`` property. There are also methods such as ``.isSuccess`` or ``.isServerError`` for checking specific response code ranges.
+
+Response headers
+----------------
+
+Response headers are available through the ``.headers`` property, which gives all headers as a sequence (not as a map, as there can be multiple headers with the same name).
+
+Individual headers can be obtained using the methods::
+
+ def header(h: String): Option[String]
+ def headers(h: String): Seq[String]
+
+There are also helper methods available to read some commonly accessed headers::
+
+ def contentType: Option[String]
+ def contentLength: Option[Long]
+
+Finally, it's possible to parse the response cookies into a sequence of the ``Cookie`` case class::
+
+ def cookies: Seq[Cookie]
+
+If the cookies from a response should be set without changes on the request, this can be done directly; see the :ref:`cookies <cookies>` section in the request definition documentation.
+
+Obtaining the response body
+---------------------------
+
+The response body can be obtained through the ``.body`` property, which has type ``Either[String, T]``. ``T`` is the body deserialized as specified in the request - see the next section on :ref:`response body specifications <responsebodyspec>`.
+
+The response body is an either as the response body can only be deserialized if the server responded with code 200. Otherwise, the response body is most probably an error message.
+
+Hence, the ``response.body`` will be a:
+
+* ``Left(errorMessage)`` if the request is successful, but response code is not 2xx.
+* ``Right(deserializedBody``) if the request is successful and the response code is 2xx.
+
+You can also forcibly get the deserialized body, risking an excepiton being thrown, using the ``response.unsafeBody`` method.
diff --git a/docs/responses/body.rst b/docs/responses/body.rst
new file mode 100644
index 0000000..da3009c
--- /dev/null
+++ b/docs/responses/body.rst
@@ -0,0 +1,86 @@
+.. _responsebodyspec:
+
+Response body specification
+===========================
+
+By default, the received response body will be read as a ``String``, using the ``UTF-8`` encoding. This is of course configurable: response bodies can be ignored, deserialized into custom types, recevied as a stream or saved to a file.
+
+How the response body will be read is part of the request definition, as already when sending the request, the backend needs to know what to do with the response. The type to which the response body should be deserialized is the second type parameter of ``RequestT``, and stored in the request definition as ``request.response``, which has type ``ResponseAs[T, S]``.
+
+Basic response specifications
+-----------------------------
+
+To conveniently specify how to deserialize the response body, a number of ``as[Type]`` methods are available. They can be used to provide a value for the requet's ``response`` modifier::
+
+ sttp.response(asByteArray)
+
+When the above request is completed and sent, it will result in a ``Response[Array[Byte]]``. Other possible response specifications are::
+
+ def ignore: ResponseAs[Unit, Nothing]
+ def asString: ResponseAs[String, Nothing]
+ def asString(encoding: String): ResponseAs[String, Nothing]
+ def asByteArray: ResponseAs[Array[Byte], Nothing]
+ def asParams: ResponseAs[Seq[(String, String)], Nothing]
+ def asParams(encoding: String): ResponseAs[Seq[(String, String)], Nothing] =
+ def asFile(file: File, overwrite: Boolean = false): ResponseAs[File, Nothing]
+ def asPath(path: Path, overwrite: Boolean = false): ResponseAs[Path, Nothing]
+
+Hence, to discard the response body, simply specify::
+
+ sttp.response(ignore)
+
+And to save the response to a file::
+
+ sttp.respone(asFile(someFile))
+
+.. note::
+
+ As the handling of response is specified upfront, there's no need to "consume" the response body. It can be safely discarded if not needed.
+
+Custom body deserializers
+-------------------------
+
+It's possible to define custom body deserializers by taking any of the built-in response specifications and mapping over them. Each ``ResponseAs`` instance has a ``map`` method, which can be used to transform it to a specification for another type. Each such value is immutable and can be used multiple times.
+
+As an example, to read the response body as an int, the following response specification can be defined (warning: this ignores the possibility of exceptions!)::
+
+ val asInt: ResponseAs[Int, Nothing] = asString.map(_.toInt)
+
+ sttp
+ .response(asInt)
+ ...
+
+To integrate with a third-party JSON library::
+
+ def parseJson(json: String): Either[JsonError, JsonAST] = ...
+ val asJson: ResponseAs[Either[JsonError, JsonAST], Nothing] = asString.map(parseJson)
+
+For some mapped response specifications available out-of-the-box, see :ref:`json support <json>`.
+
+Streaming
+---------
+
+If the backend used supports streaming (see :ref:`backends summary <backends_summary>`), it's possible to receive responses as a stream. This can be specified using the following method::
+
+ def asStream[S]: ResponseAs[S, S] = ResponseAsStream[S, S]()
+
+For example, when using the :ref:`akka-http backend <akkahttp>`::
+
+ import com.softwaremill.sttp._
+ import com.softwaremill.sttp.akkahttp._
+
+ import akka.stream.scaladsl.Source
+ import akka.util.ByteString
+
+ implicit val sttpBackend = AkkaHttpBackend()
+
+ val response: Future[Response[Source[ByteString, Any]]] =
+ sttp
+ .post(uri"...")
+ .response(asStream[Source[ByteString, Any]])
+ .send()
+
+.. note::
+
+ Unlike with non-streaming response handlers, each streaming response should be entirely consumed by client code.
+
diff --git a/docs/watch.sh b/docs/watch.sh
new file mode 100755
index 0000000..24c4372
--- /dev/null
+++ b/docs/watch.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+sphinx-autobuild . _build/html