<commit_before># Client-side HTTP sessions for akka-http

[akka-http](http://doc.akka.io/docs/akka-stream-and-http-experimental/1.0-M4/scala/http/) is an experimental Akka 
module, originating from [spray.io](http://spray.io), for building *reactive* REST services with an elegant DSL.

It has almost all of the required functionalities to serve as a backend for e.g. a single-page-application, with one
exception: session handling. This project aims to fill that gap.

## Client-side sessions

### Basic usage

Session data is stored as a cookie on the client. The content of the cookie is signed using a server secret, so that
it is not possible to alter the session data on the client side.
 
All directives require an implicit instance of a `SessionManager`, which can be created by providing a server secret.
The secret should be a long, random string. You can generate one by running `SessionUtil.randomServerSecret()`. Note
that when you change the secret, all sessions will become invalid.

````scala
val sessionConfig = SessionConfig.default("some_very_long_secret_and_random_string")
implicit val sessionManager = new SessionManager(sessionConfig)
````

The basic directives enable you to set, read and invalidate the session. To create a new client-side session, that is,
send the cookie:

````scala
path("login") {
  post {
    entity(as[String]) { body =>
      setSession(Map("key1" -> "value1", "key2" -> "value2")) { ctx =>
        ctx.complete("ok")
      }
    }
  }
}
````

Note that the size of the cookie is limited to 4KB, so you shouldn't put too much data in there (the signature takes
about 50 characters). Typically the session will contain the user id, username or a token, and the rest will be read
on the server (from a database, memcache server etc.)

You can require a session to be present or optionally require a session:

````scala
path("secret") {
  get {
    requiredSession() { session => // type: Map[String, String]
      complete { "treasure" }
    }
  }
} ~
path("open") {
  get {
    optionalSession() { session => // type: Option[Map[String, String]]
      complete { "small treasure" }
    }
  }
}
````

If a required session is not present, by default a 403 is returned. Finally, a session can be invalidated:

````scala
path("logout") {
  get {
    requiredSession() { session => 
      invalidateSession() {
        complete { "logged out" }
      }
    }
  }
}
````

You can try out a running example by running `com.softwaremill.example.Example` in the `example` project.

### Encrypting the session

It is possible to encrypt the session data by modifying the config:

````scala
val sessionConfig = SessionConfig
    .default("some_very_long_secret_and_random_string")
    .withEncryptSessionData(true)
````<commit_msg>Typed sessions readme<commit_after># Client-side HTTP sessions for akka-http

[akka-http](http://doc.akka.io/docs/akka-stream-and-http-experimental/1.0-M4/scala/http/) is an experimental Akka 
module, originating from [spray.io](http://spray.io), for building *reactive* REST services with an elegant DSL.

It has almost all of the required functionalities to serve as a backend for e.g. a single-page-application, with one
exception: session handling. This project aims to fill that gap.

## Client-side sessions

### Basic usage

Session data is stored as a cookie on the client. The content of the cookie is signed using a server secret, so that
it is not possible to alter the session data on the client side.
 
All directives require an implicit instance of a `SessionManager[T]`, which can be created by providing a server secret.
The secret should be a long, random string. You can generate one by running `SessionUtil.randomServerSecret()`. Note
that when you change the secret, all sessions will become invalid.

Sessions are typed, the `T` type parameter determines what data is stored in the session. Basic types like `String`,
`Int`, `Long`, `Float`, `Double` and `Map[String, String]` are supported out-of-the box. Support for other types
can be added by providing an implicit `SessionSerializer[T]`. For case classes, it's most convenient to implement
`ToMapSessionSerializer[T]` which should convert the instance into a `String` map (nested types are not supported on
purpose, as session data should be small & simple).

````scala
val sessionConfig = SessionConfig.default("some_very_long_secret_and_random_string")
implicit val sessionManager = new SessionManager[Long](sessionConfig)
````

The basic directives enable you to set, read and invalidate the session. To create a new client-side session, that is,
send the cookie:

````scala
path("login") {
  post {
    entity(as[String]) { body =>
      setSession(812832L) { ctx =>
        ctx.complete("ok")
      }
    }
  }
}
````

Note that the size of the cookie is limited to 4KB, so you shouldn't put too much data in there (the signature takes
about 50 characters). Typically the session will contain the user id, username or a token, and the rest will be read
on the server (from a database, memcache server etc.)

You can require a session to be present or optionally require a session:

````scala
path("secret") {
  get {
    requiredSession() { session => // type: Long, or whatever the T parameter is
      complete { "treasure" }
    }
  }
} ~
path("open") {
  get {
    optionalSession() { session => // type: Option[Long] (Option[T])
      complete { "small treasure" }
    }
  }
}
````

If a required session is not present, by default a 403 is returned. Finally, a session can be invalidated:

````scala
path("logout") {
  get {
    requiredSession() { session => 
      invalidateSession() {
        complete { "logged out" }
      }
    }
  }
}
````

You can try out a running example by running `com.softwaremill.example.Example` in the `example` project.

### Encrypting the session

It is possible to encrypt the session data by modifying the config:

````scala
val sessionConfig = SessionConfig
    .default("some_very_long_secret_and_random_string")
    .withEncryptSessionData(true)
````
{'fraction_non_alphanumeric': 0.09789978501736399, 'fraction_numerical': 0.004630395237307756, 'mean_word_length': 3.945216680294358, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1705536', 'n_tokens_mistral': 1735, 'n_tokens_neox': 1650, 'n_words': 738}