=doc first parts of HTTP model documentation
This commit is contained in:
parent
d7b4770657
commit
c6993dd279
7 changed files with 491 additions and 0 deletions
|
|
@ -7,3 +7,4 @@ Scala Documentation
|
|||
:maxdepth: 2
|
||||
|
||||
experimental/index
|
||||
scala/index-http
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http
|
||||
|
||||
import akka.stream.testkit.AkkaSpec
|
||||
import akka.actor.ActorSystem
|
||||
import akka.util.Timeout
|
||||
import scala.concurrent.duration._
|
||||
import akka.http.model._
|
||||
|
||||
class HttpServerExampleSpec
|
||||
extends AkkaSpec("akka.actor.default-mailbox.mailbox-type = akka.dispatch.UnboundedMailbox") {
|
||||
def ActorSystem(): ActorSystem = system
|
||||
|
||||
"binding example" in {
|
||||
//#bind-example
|
||||
import akka.pattern.ask
|
||||
|
||||
import akka.io.IO
|
||||
import akka.http.Http
|
||||
|
||||
import akka.stream.scaladsl.Flow
|
||||
import akka.stream.{ MaterializerSettings, FlowMaterializer }
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
import system.dispatcher
|
||||
val materializer = FlowMaterializer(MaterializerSettings())
|
||||
implicit val askTimeout: Timeout = 500.millis
|
||||
|
||||
val bindingFuture = IO(Http) ? Http.Bind(interface = "localhost", port = 8080)
|
||||
bindingFuture foreach {
|
||||
case Http.ServerBinding(localAddress, connectionStream) ⇒
|
||||
Flow(connectionStream).foreach {
|
||||
case Http.IncomingConnection(remoteAddress, requestProducer, responseConsumer) ⇒
|
||||
println("Accepted new connection from " + remoteAddress)
|
||||
|
||||
// handle connection here
|
||||
}.consume(materializer)
|
||||
}
|
||||
//#bind-example
|
||||
}
|
||||
"full-server-example" in {
|
||||
import akka.pattern.ask
|
||||
|
||||
import akka.io.IO
|
||||
import akka.http.Http
|
||||
|
||||
import akka.stream.scaladsl.Flow
|
||||
import akka.stream.{ MaterializerSettings, FlowMaterializer }
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
import system.dispatcher
|
||||
val materializer = FlowMaterializer(MaterializerSettings())
|
||||
implicit val askTimeout: Timeout = 500.millis
|
||||
|
||||
val bindingFuture = IO(Http) ? Http.Bind(interface = "localhost", port = 8080)
|
||||
|
||||
//#full-server-example
|
||||
import HttpMethods._
|
||||
|
||||
val requestHandler: HttpRequest ⇒ HttpResponse = {
|
||||
case HttpRequest(GET, Uri.Path("/"), _, _, _) ⇒
|
||||
HttpResponse(
|
||||
entity = HttpEntity(MediaTypes.`text/html`,
|
||||
<html><body>Hello world!</body></html>.toString))
|
||||
|
||||
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) ⇒ HttpResponse(entity = "PONG!")
|
||||
case HttpRequest(GET, Uri.Path("/crash"), _, _, _) ⇒ sys.error("BOOM!")
|
||||
case _: HttpRequest ⇒ HttpResponse(404, entity = "Unknown resource!")
|
||||
}
|
||||
|
||||
// ...
|
||||
bindingFuture foreach {
|
||||
case Http.ServerBinding(localAddress, connectionStream) ⇒
|
||||
Flow(connectionStream).foreach {
|
||||
case Http.IncomingConnection(remoteAddress, requestProducer, responseConsumer) ⇒
|
||||
println("Accepted new connection from " + remoteAddress)
|
||||
|
||||
Flow(requestProducer).map(requestHandler).produceTo(materializer, responseConsumer)
|
||||
}.consume(materializer)
|
||||
}
|
||||
//#full-server-example
|
||||
}
|
||||
}
|
||||
85
akka-docs-dev/rst/scala/code/docs/http/ModelSpec.scala
Normal file
85
akka-docs-dev/rst/scala/code/docs/http/ModelSpec.scala
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
package docs.http
|
||||
|
||||
//#import-model
|
||||
import akka.http.model._
|
||||
import org.scalatest.MustMatchers
|
||||
|
||||
//#import-model
|
||||
|
||||
import akka.stream.testkit.AkkaSpec
|
||||
import akka.util.ByteString
|
||||
import akka.http.model.headers.{ GenericHttpCredentials, BasicHttpCredentials }
|
||||
|
||||
class ModelSpec extends AkkaSpec {
|
||||
"construct request" in {
|
||||
//#construct-request
|
||||
import HttpMethods._
|
||||
|
||||
// construct simple GET request to `homeUri`
|
||||
val homeUri = Uri("/abc")
|
||||
HttpRequest(GET, uri = homeUri)
|
||||
|
||||
// construct simple GET request to "/index" which is converted to Uri automatically
|
||||
HttpRequest(GET, uri = "/index")
|
||||
|
||||
// construct simple POST request containing entity
|
||||
val data = ByteString("abc")
|
||||
HttpRequest(POST, uri = "/receive", entity = data)
|
||||
|
||||
// customize every detail of HTTP request
|
||||
import HttpProtocols._
|
||||
import MediaTypes._
|
||||
val userData = ByteString("abc")
|
||||
val authorization = headers.Authorization(BasicHttpCredentials("user", "pass"))
|
||||
HttpRequest(
|
||||
PUT,
|
||||
uri = "/user",
|
||||
entity = HttpEntity(`text/plain`, userData),
|
||||
headers = List(authorization),
|
||||
protocol = `HTTP/1.0`)
|
||||
//#construct-request
|
||||
}
|
||||
|
||||
"construct response" in {
|
||||
//#construct-response
|
||||
import StatusCodes._
|
||||
|
||||
// simple OK response without data created using the integer status code
|
||||
HttpResponse(200)
|
||||
|
||||
// 404 response created using the named StatusCode constant
|
||||
HttpResponse(NotFound)
|
||||
|
||||
// 404 response with a body explaining the error
|
||||
HttpResponse(404, entity = "Unfortunately, the resource couldn't be found.")
|
||||
|
||||
// A redirecting response containing an extra header
|
||||
val locationHeader = headers.Location("http://example.com/other")
|
||||
HttpResponse(Found, headers = List(locationHeader))
|
||||
|
||||
//#construct-response
|
||||
}
|
||||
|
||||
"deal with headers" in {
|
||||
//#headers
|
||||
import akka.http.model.headers._
|
||||
|
||||
// create a ``Location`` header
|
||||
val loc = Location("http://example.com/other")
|
||||
|
||||
// create an ``Authorization`` header with HTTP Basic authentication data
|
||||
val auth = Authorization(BasicHttpCredentials("joe", "josepp"))
|
||||
|
||||
// a method that extracts basic HTTP credentials from a request
|
||||
case class User(name: String, pass: String)
|
||||
def credentialsOfRequest(req: HttpRequest): Option[User] =
|
||||
for {
|
||||
Authorization(BasicHttpCredentials(user, pass)) <- req.header[headers.Authorization]
|
||||
} yield User(user, pass)
|
||||
//#headers
|
||||
|
||||
credentialsOfRequest(HttpRequest(headers = List(auth))) should be(Some(User("joe", "josepp")))
|
||||
credentialsOfRequest(HttpRequest()) should be(None)
|
||||
credentialsOfRequest(HttpRequest(headers = List(Authorization(GenericHttpCredentials("Other", Map.empty[String, String]))))) should be(None)
|
||||
}
|
||||
}
|
||||
153
akka-docs-dev/rst/scala/http-core-server.rst
Normal file
153
akka-docs-dev/rst/scala/http-core-server.rst
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
.. _http-core-server-scala:
|
||||
|
||||
HTTP Server
|
||||
===========
|
||||
|
||||
The Akka HTTP server is an embedded, stream-based, fully asynchronous, low-overhead
|
||||
HTTP/1.1 server implemented on top of `Akka Streams`_. (todo: fix link)
|
||||
|
||||
It sports the following features:
|
||||
|
||||
- Low per-connection overhead for supporting many thousand concurrent connections
|
||||
- Efficient message parsing and processing logic for high throughput applications
|
||||
- Full support for `HTTP persistent connections`_
|
||||
- Full support for `HTTP pipelining`_
|
||||
- Full support for asynchronous HTTP streaming (including "chunked" transfer encoding) accessible through an idiomatic
|
||||
reactive streams API
|
||||
- Optional SSL/TLS encryption
|
||||
|
||||
.. _HTTP persistent connections: http://en.wikipedia.org/wiki/HTTP_persistent_connection
|
||||
.. _HTTP pipelining: http://en.wikipedia.org/wiki/HTTP_pipelining
|
||||
.. _Akka streams: http://akka.io/docs/
|
||||
|
||||
|
||||
Design Philosophy
|
||||
-----------------
|
||||
|
||||
Akka HTTP server is scoped with a clear focus on the essential functionality of an HTTP/1.1 server:
|
||||
|
||||
- Connection management
|
||||
- Message parsing and header separation
|
||||
- Timeout management (for requests and connections)
|
||||
- Response ordering (for transparent pipelining support)
|
||||
|
||||
All non-core features of typical HTTP servers (like request routing, file serving, compression, etc.) are left to
|
||||
the next-higher layer in the application stack, they are not implemented by the HTTP server itself.
|
||||
Apart from general focus this design keeps the server small and light-weight as well as easy to understand and
|
||||
maintain.
|
||||
|
||||
|
||||
Basic Architecture
|
||||
------------------
|
||||
|
||||
Akka HTTP server is implemented on top of Akka streams and makes heavy use of them - in its
|
||||
implementation and also on all levels of its API.
|
||||
|
||||
On the connection level Akka HTTP supports basically the same interface as Akka streams IO: A socket binding is
|
||||
represented as a stream of incoming connections. Each connection itself is composed of an input stream of requests and
|
||||
an output consumer of responses. The application has to provide the handler to "translate" requests into responses.
|
||||
|
||||
Streaming is also supported for single message entities itself. Particular kinds of ``HttpEntity``
|
||||
subclasses provide support for fixed or streamed message entities.
|
||||
|
||||
|
||||
Starting and Stopping
|
||||
---------------------
|
||||
|
||||
An Akka HTTP server is started by sending an ``Http.Bind`` command to the `akka.http.Http`_ extension:
|
||||
|
||||
.. includecode:: code/docs/http/HttpServerExampleSpec.scala
|
||||
:include: bind-example
|
||||
|
||||
With the ``Http.Bind`` command you specify the interface and port to bind to and register interest in handling incoming
|
||||
HTTP connections. Additionally the ``Http.Bind`` command also allows you to define socket options as well as a larger number
|
||||
of settings for configuring the server according to your needs.
|
||||
|
||||
The sender of the ``Http.Bind`` command (e.g. an actor you have written) will receive an ``Http.ServerBinding`` reply
|
||||
after the HTTP layer has successfully started the server at the respective endpoint. In case the bind fails (e.g.
|
||||
because the port is already busy) a ``Failure`` message is dispatched instead. As shown in the above example this works
|
||||
well with the ask pattern and Future operations.
|
||||
|
||||
The ``Http.ServerBinding`` informs the binder of the actual local address of the bound socket and it contains a
|
||||
stream of incoming connections of type ``Producer[Http.IncomingConnection]``. Connections are handled by subscribing
|
||||
to the connection stream and handling the incoming connections.
|
||||
|
||||
The binding is released and the underlying listening socket is closed when all subscribers of the
|
||||
``Http.ServerBinding.connectionStream`` have cancelled their subscriptions.
|
||||
|
||||
.. _akka.http.Http: @github@/akka-http-core/src/main/scala/akka/http/Http.scala
|
||||
|
||||
|
||||
Request-Response Cycle
|
||||
----------------------
|
||||
|
||||
When a new connection has been accepted it will be published by the ``Http.ServerBinding.connectionStream`` as an
|
||||
``Http.IncomingConnection`` which consists of the remote address, a ``requestProducer``, and a ``responseConsumer``.
|
||||
|
||||
Handling requests in this model means connecting the ``requestProducer`` stream with an application-defined component that
|
||||
maps requests to responses which then feeds into the ``responseConsumer``:
|
||||
|
||||
.. includecode:: code/docs/http/HttpServerExampleSpec.scala
|
||||
:include: full-server-example
|
||||
|
||||
In this case, a request is handled by transforming the request stream with a function ``HttpRequest => HttpResponse``
|
||||
using Akka stream's ``map`` operator. Depending on the use case, arbitrary other ways of connecting are conceivable using
|
||||
Akka stream's operators (e.g using ``mapFuture`` to allow parallel processing of several requests when HTTP pipelining is
|
||||
enabled).
|
||||
|
||||
It's the application's responsibility to feed responses into the ``responseConsumer`` in the same order as the respective
|
||||
requests have come in. Also, each request must result in exactly one response. Using stream operators like ``map`` or
|
||||
``mapFuture`` will automatically fulfill this requirement.
|
||||
|
||||
Streaming request/response entities
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Streaming of HTTP message entities is supported through subclasses of ``HttpEntity``. You need to be able to deal
|
||||
with streamed entities when receiving a request as well as when constructing responses. See :ref:`HttpEntity` for
|
||||
a description of the alternatives.
|
||||
|
||||
(todo): Link to :ref:`http-routing-scala` for (un-)marshalling facilities.
|
||||
|
||||
|
||||
Closing a connection
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The HTTP connection will be closed when the ``responseConsumer`` gets completed or when the ``requestProducer``'s
|
||||
subscription was cancelled and no more responses are pending.
|
||||
|
||||
You can also use the value of the ``Connection`` header of a response as described below to give a hint to the
|
||||
implementation to close the connection after the completion of the response.
|
||||
|
||||
HTTP Headers
|
||||
------------
|
||||
|
||||
When the Akka HTTP server receives an HTTP request it tries to parse all its headers into their respective
|
||||
model classes. No matter whether this succeeds or not, the connection actor will always pass on all
|
||||
received headers to the application. Unknown headers as well as ones with invalid syntax (according to the header
|
||||
parser) will be made available as ``RawHeader`` instances. For the ones exhibiting parsing errors a warning message is
|
||||
logged depending on the value of the ``illegal-header-warnings`` config setting.
|
||||
|
||||
Some common headers are treated specially in the model and in the implementation and should not occur in the ``headers``
|
||||
field of an HTTP message:
|
||||
|
||||
- ``Content-Type``: Use the ``contentType`` field of the ``HttpEntity`` subclasses to set or determine the content-type
|
||||
on an entity.
|
||||
- ``Transfer-Encoding``: The ``Transfer-Encoding`` is represented by subclasses of ``HttpEntity``.
|
||||
- ``Content-Length``: The ``Content-Length`` header is represented implicitly by the choice of an ``HttpEntity`` subclass:
|
||||
A Strict entity determines the Content-Length by the length of the data provided. A Default entity has an explicit
|
||||
``contentLength`` field which specifies the amount of data the streaming producer will produce. Chunked and CloseDelimited
|
||||
entities don't need to define a length.
|
||||
- ``Server``: The ``Server`` header is usually added automatically and it's value can be configured. An application can
|
||||
decide to provide a custom ``Server`` header by including an explicit instance in the response.
|
||||
- ``Date``: The ``Date`` header is added automatically and will be ignored if supplied manually.
|
||||
- ``Connection``: When sending out responses the connection actor watches for a ``Connection`` header set by the
|
||||
application and acts accordingly, i.e. you can force the connection actor to close the connection after having sent
|
||||
the response by including a ``Connection("close")`` header. To unconditionally force a connection keep-alive you can
|
||||
explicitly set a ``Connection("Keep-Alive")`` header. If you don't set an explicit ``Connection`` header the
|
||||
connection actor will keep the connection alive if the client supports this (i.e. it either sent a
|
||||
``Connection: Keep-Alive`` header or advertised HTTP/1.1 capabilities without sending a ``Connection: close`` header).
|
||||
|
||||
SSL Support
|
||||
-----------
|
||||
|
||||
(todo)
|
||||
151
akka-docs-dev/rst/scala/http-model.rst
Normal file
151
akka-docs-dev/rst/scala/http-model.rst
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
.. _http-model-scala:
|
||||
|
||||
HTTP Model
|
||||
==========
|
||||
|
||||
The akka HTTP model contains a mostly immutable, case-class based model of the major HTTP data structures,
|
||||
like HTTP requests, responses and common headers. It also includes a parser for the latter, which is able to construct
|
||||
the more structured header models from raw unstructured header name/value pairs.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Since akka-http-core provides the central HTTP data structures you will find the following import in quite a
|
||||
few places around the code base (and probably your own code as well):
|
||||
|
||||
.. includecode:: code/docs/http/ModelSpec.scala
|
||||
:include: import-model
|
||||
|
||||
This brings in scope all of the relevant things that are defined here and that you’ll want to work with, mainly:
|
||||
|
||||
- ``HttpRequest`` and ``HttpResponse``, the central message model
|
||||
- ``headers``, the package containing all the predefined HTTP header models and supporting types
|
||||
- Supporting types like ``Uri``, ``HttpMethods``, ``MediaTypes``, ``StatusCodes``, etc.
|
||||
|
||||
A common pattern is that the model of a certain entity is represented by an immutable type (class or trait),
|
||||
while the actual instances of the entity defined by the HTTP spec live in an accompanying object carrying the name of
|
||||
the type plus a trailing plural 's'.
|
||||
|
||||
For example:
|
||||
|
||||
- Defined HttpMethod instances live in the HttpMethods object.
|
||||
- Defined HttpCharset instances live in the HttpCharsets object.
|
||||
- Defined HttpEncoding instances live in the HttpEncodings object.
|
||||
- Defined HttpProtocol instances live in the HttpProtocols object.
|
||||
- Defined MediaType instances live in the MediaTypes object.
|
||||
- Defined StatusCode instances live in the StatusCodes object.
|
||||
|
||||
HttpRequest / HttpResponse
|
||||
--------------------------
|
||||
|
||||
``HttpRequest`` and ``HttpResponse`` are the basic case classes representing HTTP messages.
|
||||
|
||||
An ``HttpRequest`` consists of
|
||||
|
||||
- method (GET, POST, etc.)
|
||||
- URI
|
||||
- protocol
|
||||
- headers
|
||||
- entity (body data)
|
||||
|
||||
Here are some examples how to construct an ``HttpRequest``:
|
||||
|
||||
.. includecode:: code/docs/http/ModelSpec.scala
|
||||
:include: construct-request
|
||||
|
||||
All parameters of ``HttpRequest`` have default values set, so e.g. ``headers`` don't need to be specified
|
||||
if there are none. Many of the parameters types (like ``HttpEntity`` and ``Uri``) define implicit conversions
|
||||
for common use cases to simplify the creation of request and response instances.
|
||||
|
||||
An ``HttpResponse`` consists of
|
||||
|
||||
- status code
|
||||
- protocol
|
||||
- headers
|
||||
- entity
|
||||
|
||||
Here are some examples how to construct an ``HttpResponse``:
|
||||
|
||||
.. includecode:: code/docs/http/ModelSpec.scala
|
||||
:include: construct-response
|
||||
|
||||
Aside from the simple ``HttpEntity`` constructors to create an entity from a fixed ``ByteString`` shown here,
|
||||
subclasses of ``HttpEntity`` allow data to be specified as a stream of data which is explained in the next section.
|
||||
|
||||
.. _HttpEntity:
|
||||
|
||||
HttpEntity
|
||||
----------
|
||||
|
||||
An ``HttpEntity`` carries the content of a request together with its content-type which is needed to interpret the raw
|
||||
byte data.
|
||||
|
||||
Akka HTTP provides several kinds of entities to support static and streaming data for the different kinds of ways
|
||||
to transport streaming data with HTTP. There are four subtypes of HttpEntity:
|
||||
|
||||
|
||||
HttpEntity.Strict
|
||||
An entity which wraps a static ``ByteString``. It represents a standard, non-chunked HTTP message with ``Content-Length``
|
||||
set.
|
||||
|
||||
|
||||
HttpEntity.Default
|
||||
A streaming entity which needs a predefined length and a ``Producer[ByteString]`` to produce the body data of
|
||||
the message. It represents a standard, non-chunked HTTP message with ``Content-Length`` set. It is an error if the
|
||||
provided ``Producer[ByteString]`` doesn't produce exactly as many bytes as specified. On the wire, a Strict entity
|
||||
and a Default entity cannot be distinguished. However, they offer a valuable alternative in the API to distinguish
|
||||
between strict and streamed data.
|
||||
|
||||
|
||||
HttpEntity.Chunked
|
||||
A streaming entity of unspecified length that uses `Chunked Transfer Coding`_ for transmitting data. Data is
|
||||
represented by a ``Producer[ChunkStreamPart]``. A ``ChunkStreamPart`` is either a non-empty ``Chunk`` or a ``LastChunk``
|
||||
containing optional trailer headers. The stream must consist of 0..n ``Chunked`` parts and can be terminated by an
|
||||
optional ``LastChunk`` part (which carries optional trailer headers).
|
||||
|
||||
|
||||
HttpEntity.CloseDelimited
|
||||
A streaming entity of unspecified length that is delimited by closing the connection ("Connection: close"). Note,
|
||||
that this entity type can only be used in an ``HttpResponse``.
|
||||
|
||||
Entity types ``Strict``, ``Default``, and ``Chunked`` are a subtype of ``HttpEntity.Regular`` which allows to use them for
|
||||
requests and responses. In contrast, ``HttpEntity.CloseDelimited`` can only be used for responses.
|
||||
|
||||
Streaming entity types (i.e. all but ``Strict``) cannot be shared or serialized. To create a strict, sharable copy of an
|
||||
entity or message use ``HttpEntity.toStrict`` or ``HttpMessage.toStrict`` which returns a Future of the object with the
|
||||
body data collected into a ``ByteString``.
|
||||
|
||||
.. _Chunked Transfer Coding: http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-26#section-4.1
|
||||
|
||||
The ``HttpEntity`` companion object contains several helper constructors to create entities from common types easily.
|
||||
|
||||
You can pattern match over the subtypes of ``HttpEntity`` if you want to provide special handling for each of the
|
||||
subtypes. However, in many cases a recipient of an `HttpEntity` doesn't care about of which subtype an entity is
|
||||
(and how data is transported exactly on the HTTP layer). Therefore, a general ``HttpEntity.dataBytes`` is provided
|
||||
which allows access to the data of an entity regardless of its concrete subtype.
|
||||
|
||||
.. note::
|
||||
|
||||
When to use which subtype?
|
||||
- Use Strict if the amount of data is small and it is already in the heap (or even available as a ``ByteString``)
|
||||
- Use Default if the data is generated by a streaming data source and the size of the data is fixed
|
||||
- Use Chunked to support a data stream of unknown length
|
||||
- Use CloseDelimited as an alternative to Chunked e.g. if chunked transfer encoding isn't supported by a peer.
|
||||
|
||||
Header model
|
||||
------------
|
||||
|
||||
Akka HTTP contains a rich model of the common HTTP headers. Parsing and rendering is done automatically so that
|
||||
applications don't need to care for the actual syntax of headers. Headers not modelled explicitly are represented
|
||||
as a ``RawHeader``.
|
||||
|
||||
See these examples of how to deal with headers:
|
||||
|
||||
.. includecode:: code/docs/http/ModelSpec.scala
|
||||
:include: headers
|
||||
|
||||
Parsing / Rendering
|
||||
-------------------
|
||||
|
||||
Parsing and rendering of HTTP data structures is heavily optimized and for most types there's currently no public API
|
||||
provided to parse (or render to) Strings or byte arrays.
|
||||
6
akka-docs-dev/rst/scala/http-routing.rst
Normal file
6
akka-docs-dev/rst/scala/http-routing.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.. _http-routing-scala:
|
||||
|
||||
HTTP Routing
|
||||
============
|
||||
|
||||
(todo)
|
||||
9
akka-docs-dev/rst/scala/index-http.rst
Normal file
9
akka-docs-dev/rst/scala/index-http.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
HTTP
|
||||
====
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
http-model
|
||||
http-core-server
|
||||
http-routing
|
||||
Loading…
Add table
Add a link
Reference in a new issue