{"id":19589431,"url":"https://github.com/spray/spray-can","last_synced_at":"2025-04-27T12:32:24.888Z","repository":{"id":57715490,"uuid":"2340363","full_name":"spray/spray-can","owner":"spray","description":"A low-overhead, high-performance, fully async HTTP 1.1 server and client library implemented entirely in Scala on top of Akka","archived":false,"fork":false,"pushed_at":"2012-10-16T13:01:31.000Z","size":585,"stargazers_count":153,"open_issues_count":8,"forks_count":19,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-03-25T22:12:36.005Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://spray.io","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spray.png","metadata":{"files":{"readme":"README.markdown","changelog":"CHANGELOG","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-09-07T08:42:32.000Z","updated_at":"2024-03-25T22:12:36.006Z","dependencies_parsed_at":"2022-09-03T08:13:23.827Z","dependency_job_id":null,"html_url":"https://github.com/spray/spray-can","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spray%2Fspray-can","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spray%2Fspray-can/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spray%2Fspray-can/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spray%2Fspray-can/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spray","download_url":"https://codeload.github.com/spray/spray-can/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224069648,"owners_count":17250509,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-11T08:18:27.287Z","updated_at":"2024-11-11T08:18:27.990Z","avatar_url":"https://github.com/spray.png","language":"Scala","funding_links":[],"categories":["网络编程"],"sub_categories":["Spring Cloud框架"],"readme":"## Deprecation Note\n\nThis repository contains the sources of an older version of *spray-can* (targeted at Scala 2.9.x and Akka 1.3.x).\nIf you are looking for the latest version please turn to the main spray site at http://spray.io.\n\n---\n\n_spray-can_ is a low-overhead, high-performance, fully asynchronous HTTP 1.1 server and client library\nimplemented entirely in [Scala] on top of [Akka].\n\nBoth, the _spray-can_ server and the _spray-can_ client, sport the following features:\n  \n* Low per-connection overhead for supporting thousands of concurrent connections\n* Efficient message parsing and processing logic for high throughput applications (\u003e 50K requests/sec on ordinary consumer hardware)\n* Full support for HTTP/1.1 persistant connections\n* Full support for message pipelining\n* Full support for asynchronous HTTP streaming (i.e. \"chunked\" transfer encoding)\n* Akka-Actor and -Future based architecture for easy integration into your Akka applications\n* No dependencies except for JavaSE 6, Scala 2.9 and [Akka] 1.3.1 (actors module, in 'provided' scope).\n\n\n## Basic Architecture \u0026 Design Philosophy\n\nThe _spray-can_ HttpServer is implemented as an [Akka] actor running on a single, private thread managing a Java\nNIO selector. Incoming HTTP requests are dispatched as immutable messages to a service actor provided by the\napplication. Requests are completed by calling a responder function passed along (in continuation style) with the\nrequest message.\n\n_spray-can_ is scoped with a clear focus on the essential functionality of any HTTP 1.1 server:\n\n* Connection management\n* Message parsing and header separation\n* Timeout management (for requests and connections)\n* Response ordering (for transparent pipelining support)\n\nAll non-core features of typical HTTP servers (like request routing, file serving, compression, etc.) are left to the\nnext layer in the application stack, they are not implemented by _spray-can_ itself. Apart from general focus this design\nkeeps _spray-can_ small and light-weight as well as easy to understand and to maintain.\nIt also makes a _spray-can_ HttpServer a perfect \"container\" for a [spray-server] application, since _spray-can_ and\n_spray-server_ nicely complement and interface into each other. _spray-server_ supports _spray-can_ HttpServer out of\nthe box.\n\n(Everything in this section is also valid in analogy for the _spray-can_ HttpClient implementation.)\n\n\n### Installation\n\n_spray-can_ is available from the [repo.spray.cc] repository.\nThe latest release is `0.9.3` and is built against Scala 2.9.1 and Akka 1.3.1.\n\nIf you use SBT you can include _spray-can_ in your project with\n\n    \"cc.spray\" %%  \"spray-can\" % \"0.9.3\"\n\nApart from Scala, Akka and SLF4J _spray-can_ has no dependencies.\n\n\n## Getting Started\n\nThe easiest way to get started with _spray-can_ is to try out the `server-example` and/or the `client-example` that's\npart of the _spray-can_ codebase:\n\n1. Git-clone this repository:\n\n        $ git clone git://github.com/spray/spray-can.git my-project\n\n2. Change directory into your clone:\n\n        $ cd my-project\n\n3. Launch [SBT][] and run the server example:\n\n        $ sbt \"project server-example\" run\n\n4. Browse to \u003chttp://127.0.0.1:8080\u003e and play around with the sample \"app\".\n\n5. Run the client example:\n\n        $ sbt \"project client-example\" run\n\n6. Start hacking on the sources in\n\n   * `server-example/src/main/scala/cc/spray/can/example/` and/or\n   * `client-example/src/main/scala/cc/spray/can/example/`\n\n\n## Server\n\n### Basic Usage\n\nThe _spray-can_ HTTP server is really easy to use. All you need to do is start a new [HttpServer] actor as well as\nan actor holding your custom request handling logic. Ideally these actors should be supervised:\n\n    Supervisor(\n      SupervisorConfig(\n        OneForOneStrategy(List(classOf[Exception]), 3, 100),\n        List(\n          Supervise(Actor.actorOf(new MyService()), Permanent),\n          Supervise(Actor.actorOf(new HttpServer()), Permanent)\n        )\n      )\n    )\n\nYou can pass a [ServerConfig] instance to the [HttpServer] constructor. If you don't _spray-can_ looks for a server\nconfiguration in your applications `akka.conf` file and uses default settings for anything not specified there.\nBy default your service actor needs to have the id `spray-root-service` in order to be found by the `HttpServer`.\nAfter being started the server actor will accept new HTTP connections on the configured host interface (and port) and\ndispatch all incoming HTTP requests as [RequestContext] messages to your service actor. You can take a look at the\nserver-examples [TestService] implementation for some example of what basic request handling with _spray-can_ might\nlook like.\n\n\n### HTTP Headers\n\nThe _spray-can_ server always passes all received headers on to your application. Additionally the values of the\nfollowing request headers are interpreted by the server itself:\n\n* `Connection`\n* `Content-Length`\n* `Transfer-Encoding`\n\nAll other headers are of no interest to the server layer.\n\nWhen sending out responses the server watches for a `Connection` header that your application might set and acts\naccordingly. I.e. you can force _spray-can_ to close the connection after having sent the response by including an\n`HttpHeader(\"Connection\", \"close\")`. To unconditionally force a connection keep-alive you can explicitly set a\n`HttpHeader(\"Connection\", \"Keep-Alive\")` header. If you don't set an explicit `Connection` header the server will keep\nthe connection alive if the client supports this (i.e. it either sent a \"Connection: Keep-Alive\" header or specified\nHTTP/1.1 capabilities without sending a \"Connection: close\" header).\n\nYour [HttpResponse] instances must not include explicit `Content-Length`, `Transfer-Encoding` or `Date` headers, since\n_spray-can_ sets these automatically.\n\n\n### Timeouts\n\nIf configured with a non-zero `requestTimeout` setting the _spray-can_ [HttpServer] will watch for request timeouts.\nIf your application logic does not complete a request by either calling `responder.complete` or\n`responder.startChunkedResponse` on the incoming [RequestContext] within the configured timeout period the `HttpServer`\ndispatches a [Timeout] message to the configured timeout actor (which may well be identical to your service actor).\nThe application then has another chance to complete the request, this time within the configured `timeoutTimeout`\nperiod. Only if the request is still uncompleted after this time period the `HttpServer` will complete the request\nitself with the result from its `timeoutTimeoutResponse` method (which you may override should the need arise).\n\nIf the [ServerConfig] has a non-zero `idleTimeout` the `HttpServer` will close idle connections after the respective\ntime period.\n\n\n### Pipelining\n\nHTTP pipelining is fully supported and completely transparent to your application. I.e. the client is allowed to send\na whole sequence of requests in a row without first waiting for responses. The _spray-can_ `HttpServer` dispatches\nsuch pipelined requests to your service actor just as any other. However, since in many asynchronous applications\nresponse times can be somewhat undeterministic _spray-can_ will take care of properly ordering all responses coming in\nfrom your application before sending them out to \"the wire\". I.e. your application will \"see\" requests in the order\nthey are coming in but is not required to uphold this order when generating responses.\n\n\n### Streaming / Chunked Messages\n\nHTTP/1.1 defines the \"chunked\" transfer encoding for HTTP messages (requests and responses), which allows for the\nsending of very large (even \"infinite\") HTTP requests or responses. Normally the client or server sending an\nHTTP message needs to know the size of the message _before_ sending it. Chunked transfer encoding removes this\nrequirement, i.e. the client or server can start sending the message before the complete length is known (which might\nbe useful when transferring things like a live video or audio stream).\n\n_spray-can_ fully supports chunked HTTP requests and responses in an asynchronous fashion.\n\n\n#### Receiving Chunked Requests\n\nWhen the _spray-can_ `HttpServer` receives the first bits of a chunked request from a client it starts a new Akka actor\nfor handling the different parts of the request. The [ServerConfig] contains a `streamActorCreator` member which\ncan hold a custom function performing the actual actor creation. _spray-can_ takes care of properly starting and\nstopping the actor your custom function created as well as dispatching [MessageChunk] and [ChunkedRequestEnd] messages\nto it. If you do not supply a custom `streamActorCreator` _spray-can_ uses a [BufferingRequestStreamActor] for incoming\nchunked requests to transparently buffer and assemble regular [HttpResponse] instances before dispatching them to the\nregular service actor.\n\n\n#### Sending Chunked Responses\n\nYour application can decide to respond to a request with a chunked response rather than a \"traditional\" one. This is\ndone via the `startChunkedResponse` method of the `responder` member of the incoming [RequestContext]. This method\nreturns a [ChunkedResponder] that allows for the sending of the individual message chunks as well as finalization of the\nresponse.\n\n\n### Shutting Down\n\nThe best way to shut down a _spray-can_ HTTP server instance is to send it an Akka `PoisonPill` message.\nThis will ensure the proper closing of all open connections as well as the freeing all other occupied resources.\nSimply stopping the `HttpServer` actor by calling `stop()` (or `Actor.registry.shutdownAll()`) can sometimes lead to the\nserver thread not properly terminating.\n\n\n## Client\n\nThe _spray-can_ `HttpClient` is the natural counterpart of the `HttpServer`. It shares all core features as well as the\nbasic \"low-level\" philosophy with the server.\n\n\n### Basic Usage\n\nJust like the `HttpServer` the `HttpClient` is implemented as an Akka actor running on a single, private thread.\nSo, in order to use it you first need to create and start it:\n\n    Supervisor(\n      SupervisorConfig(\n        OneForOneStrategy(List(classOf[Exception]), 3, 100),\n        List(Supervise(Actor.actorOf(new HttpClient()), Permanent))\n      )\n    )\n\nYou can pass a [ClientConfig] instance to the [HttpClient] constructor. If you don't _spray-can_ looks for a client\nconfiguration in your applications `akka.conf` file and uses default settings for anything not specified there.\nAfter being started the client actor will wait for [Connect] messages from your application, which it responds with\nan object implementing the [HttpConnection] trait. Its scaladoc API documentation should give you a pretty good idea\nof how to use an [HttpConnection] instance for sending requests and receive responses.\n\nAs you can see from this API the _spray-can_ `HttpClient` works on the basis of individual connections. There is no\nhigher-level support for automatic connection pooling and such, since this is considered the responsibility of the\nnext-higher application layer.\n\n\n### Timeouts\n\nIf configured with a non-zero `requestTimeout` setting the _spray-can_ [HttpClient] will watch for request timeouts.\nIf the server does not respond within the configured timeout period a respective [HttpClientException] instance will\nbe created and delivered to either the receiver actor or the response future.\nAdditionally the `HttpClient` will automatically close idle connections if the configured `idleTimeout` is non-zero.\n\n\n### Pipelining\n\nIf you know that the HTTP server your application connects to supports request pipelining you can send several requests\nin a row without first waiting for responses to come in. The **HttpDialog DSL** (see below) might make working with\npersistant connections and request pipelinging a bit easier.\n\n\n### Streaming / Chunked Messages\n\nJust like the `HttpServer` the `HttpClient` supports sending chunked requests as well as receiving chunked responses\nin an asynchronous fashion. The scaladoc API documentation of the [HttpConnection] trait should be rather\nself-explanatory with regard to its usage.\n\n\n### HttpDialog DSL\n\nAs a thin layer on top of the `HttpClient` _spray-can_ provides a convenience mini-DSL that makes working with\nHTTP connections a bit easier. It is probably best explained by example.\n\nThe following snippet shows a minimal, single-request [HttpDialog]:\n\n    import HttpClient._\n    val response: Future[HttpResponse] =\n          HttpDialog(\"github.com\")\n          .send(HttpRequest(method = GET, uri = \"/\"))\n          .end\n\nA non-pipelined two-request dialog:\n\n    val responses: Future[Seq[HttpResponse]] =\n          HttpDialog(\"example.com\")\n          .send(HttpRequest(POST, \"/shout\").withBody(\"yeah!\"))\n          .awaitResponse\n          .send(HttpRequest(PUT, \"/count\").withBody(\"42\"))\n          .end\n\nA pipelined three-request dialog:\n\n    val responses: Future[Seq[HttpResponse]] =\n          HttpDialog(host = \"img.example.com\", port = 8888)\n          .send(HttpRequest(GET, \"a.gif\"))\n          .send(HttpRequest(GET, \"b.gif\"))\n          .send(HttpRequest(GET, \"c.gif\"))\n          .end\n\nA request -\u003e response -\u003e request dialog:\n\n    val response: Future[HttpResponse] =\n          HttpDialog(\"example.com\")\n          .send(HttpRequest(GET, \"/ping\"))\n          .reply(response =\u003e HttpRequest(GET, \"/ping2\", body = response.body))\n          .end\n\nNote that the explicit result type annotations are only shown here for documentation. They can be inferred and are\ntherefore not required.\n\n\n### Shutting Down\n\nThe best way to shut down a _spray-can_ HTTP client instance is to send it an Akka `PoisonPill` message.\nThis will ensure the proper closing of all open connections as well as the freeing all other occupied resources.\nSimply stopping the `HttpClient` actor by calling `stop()` (or `Actor.registry.shutdownAll()`) can sometimes lead to\nthe client thread not properly terminating.\n\n\n## Support\n\nMany questions might already be answered by the _spray-can_ [API documentation].\n\nYou can also turn to the active \u003chttp://groups.google.com/group/spray-user\u003e mailing list for support.\n\n\n## License\n\n_spray-can_ is licensed under [APL 2.0].\n\n\n## Patch Policy\n\nFeedback and contributions to the project, no matter what kind, are always very welcome.\nHowever, patches can only be accepted from their original author.\nAlong with any patches, please state that the patch is your original work and that you license the work to the\n_spray-can_ project under the project’s open source license.\n\n  [Scala]: http://www.scala-lang.org/\n  [Akka]: http://akka.io\n  [repo.spray.cc]: http://repo.spray.cc\n  [spray-server]: http://spray.cc\n  [SBT]: https://github.com/harrah/xsbt/wiki\n  [HttpServer]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.HttpServer\n  [ServerConfig]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.ServerConfig\n  [RequestContext]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.RequestContext\n  [TestService]: https://github.com/spray/spray-can/blob/master/server-example/src/main/scala/cc/spray/can/example/TestService.scala\n  [HttpResponse]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.HttpResponse\n  [Timeout]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.Timeout\n  [MessageChunk]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.MessageChunk\n  [ChunkedRequestEnd]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.ChunkedRequestEnd\n  [HttpClientException]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.HttpClientException\n  [BufferingRequestStreamActor]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.BufferingRequestStreamActor\n  [ChunkedResponder]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.ChunkedResponder\n  [ClientConfig]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.ClientConfig\n  [HttpClient]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.HttpClient\n  [Connect]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.Connect\n  [HttpConnection]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.HttpConnection\n  [HttpDialog]: http://spray.github.com/spray/api/spray-can/index.html#cc.spray.can.HighLevelHttpClient$HttpDialog\n  [API documentation]: http://spray.github.com/spray/api/spray-can/index.html\n  [APL 2.0]: http://www.apache.org/licenses/LICENSE-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspray%2Fspray-can","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspray%2Fspray-can","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspray%2Fspray-can/lists"}