scamper
package scamper
HTTP Messages
At the core Scamper is HttpMessage, which is a trait that defines the fundamental characteristics of an HTTP message. HttpRequest and HttpResponse extend the specification to define characteristics specific to their respective message types.
An HttpRequest is created using one of the factory methods defined in its companion object. Or you can start with a RequestMethod and use builder methods to further define the request.
import scamper.Header import scamper.Implicits.stringToUri import scamper.RequestMethods.GET val request = GET("/motd").withHeaders( Header("Host", "localhost:8080"), Header("Accept", "text/plain") ) printf("Request Method: %s%n", request.method) printf("Target URI: %s%n", request.target) request.headers.foreach(println) val host: Option[String] = request.getHeaderValue("Host")
An HttpResponse is created using one of the factory methods defined in its companion object. Or you can start with a ResponseStatus and use builder methods to further define the response.
import scamper.{ BodyParsers, Header } import scamper.Implicits.stringToEntity import scamper.ResponseStatuses.Ok val response = Ok("There is an answer.").withHeaders( Header("Content-Type", "text/plain"), Header("Connection", "close") ) printf("Status Code: %s%n", response.status.code) printf("Reason Phrase: %s%n", response.status.reason) response.headers.foreach(println) val contentType: Option[String] = response.getHeaderValue("Content-Type") implicit val parser = BodyParsers.text() printf("Body: %s%n", response.as[String])
- Alphabetic
- By Inheritance
- scamper
- AnyRef
- Any
- Hide All
- Show All
- Public
- All
Package Members
- package auth
Provides access to authethentication types and headers.
Provides access to authethentication types and headers.
Challenges and Credentials
When working with authentication, a
Challenge
is presented in the response, andCredentials
in the request. Each of these has an assigned scheme, which is associated with either a token or a set of parameters.import scamper.Implicits.stringToUri import scamper.RequestMethods.GET import scamper.ResponseStatuses.Unauthorized import scamper.auth.{ Authorization, Challenge, Credentials, WwwAuthenticate } // Present response challenge (scheme and parameters) val challenge = Challenge("Bearer", "realm" -> "developer") val res = Unauthorized().withWwwAuthenticate(challenge) // Present request credentials (scheme and token) val credentials = Credentials("Bearer", "QWxsIEFjY2VzcyEhIQo=") val req = GET("/dev/projects").withAuthorization(credentials)
Basic Authentication
There are subclasses defined for Basic authentication:
BasicChallenge
andBasicCredentials
.import scamper.Implicits.stringToUri import scamper.RequestMethods.GET import scamper.ResponseStatuses.Unauthorized import scamper.auth.{ Authorization, BasicChallenge, BasicCredentials, WwwAuthenticate } // Provide realm and optional parameters val challenge = BasicChallenge("admin", "title" -> "Admin Console") val res = Unauthorized().withWwwAuthenticate(challenge) // Provide user and password val credentials = BasicCredentials("sa", "l3tm31n") val req = GET("/admin/users").withAuthorization(credentials)
In addition, there are methods for Basic authentication defined in the header classes.
import scamper.Implicits.stringToUri import scamper.RequestMethods.GET import scamper.ResponseStatuses.Unauthorized import scamper.auth.{ Authorization, WwwAuthenticate } // Provide realm and optional parameters val res = Unauthorized().withBasic("admin", "title" -> "Admin Console") // Access basic auth in response printf(s"Realm: %s%n", res.basic.realm) printf(s"Title: %s%n", res.basic.params("title")) // Provide user and password val req = GET("/admin/users").withBasic("sa", "l3tm3m1n") // Access basic auth in request printf(s"User: %s%n", req.basic.user) printf(s"Password: %s%n", req.basic.password)
Bearer Authentication
There are subclasses defined for Bearer authentication:
BearerChallenge
andBearerCredentials
. And, similar to Basic, there are Bearer-specific methods available in the header classes.import scamper.Implicits.stringToUri import scamper.RequestMethods.GET import scamper.ResponseStatuses.Unauthorized import scamper.auth.{ Authorization, WwwAuthenticate } // Provide challenge parameters val res = Unauthorized().withBearer( "scope" -> "user profile", "error" -> "invalid_token", "error_description" -> "Expired access token" ) // Print optional realm parameter res.bearer.realm.foreach(println) // Print scope from space-delimited parameter val scope: Seq[String] = res.bearer.scope scope.foreach(println) // Print error parameters res.bearer.error.foreach(println) res.bearer.errorDescription.foreach(println) res.bearer.errorUri.foreach(println) // Test for error conditions println(res.bearer.isInvalidToken) println(res.bearer.isInvalidRequest) println(res.bearer.isInsufficientScope) // Create request with Bearer token val req = GET("/users").withBearer("R290IDUgb24gaXQhCg==")
- package client
Provides HTTP client implementation.
Provides HTTP client implementation.
Using HTTP Client
The
HttpClient
object can be used to send a request and handle the response.import scamper.BodyParsers import scamper.Implicits.stringToUri import scamper.RequestMethods.GET import scamper.client.HttpClient implicit val parser = BodyParsers.text() def getMessageOfTheDay(): Either[Int, String] = { val req = GET("localhost:8080/motd") // Send request and handle response HttpClient.send(req) { res => res.status.isSuccessful match { case true => Right(res.as[String]) case false => Left(res.status.code) } } }
Note the request must be created with an absolute URI to make effective use of the client.
Creating HTTP Client
When using the
HttpClient
object as the client, it creates an instance ofHttpClient
for one-time usage. If you plan to send multiple requests, you can create and maintain a reference to an instance, and use it as the client. With that, you also get access to methods corresponding to the standard HTTP request methods.import scamper.BodyParsers import scamper.Implicits.stringToUri import scamper.client.HttpClient implicit val parser = BodyParsers.text() // Create HttpClient instance val client = HttpClient(bufferSize = 4096, readTimeout = 3000) def getMessageOfTheDay(): Either[Int, String] = { // Use client instance client.get("http://localhost:8080/motd") { res => res.status.isSuccessful match { case true => Right(res.as[String]) case false => Left(res.status.code) } } }
And if the client is declared as an implicit value, you can make use of
send()
on the request itself.import scamper.BodyParsers import scamper.Implicits.stringToUri import scamper.RequestMethods.GET import scamper.client.HttpClient import scamper.client.Implicits.ClientHttpRequestType // Adds send method to request import scamper.headers.{ Accept, AcceptLanguage } import scamper.types.Implicits.{ stringToMediaRange, stringToLanguageRange } implicit val client = HttpClient(bufferSize = 8192, readTimeout = 1000) implicit val parser = BodyParsers.text(4096) GET("http://localhost:8080/motd") .withAccept("text/plain") .withAcceptLanguage("en-US; q=0.6", "fr-CA; q=0.4") .send(res => println(res.as[String])) // Send request and print response
- package cookies
Provides specialized access to message cookies.
Provides specialized access to message cookies.
Request Cookies
In HttpRequest, cookies are stringed together in the Cookie header. You can access them using the extension methods provided by RequestCookies, with each cookie represented as PlainCookie.
import scamper.Implicits.stringToUri import scamper.RequestMethods.GET import scamper.cookies.{ PlainCookie, RequestCookies } // Build request with cookies val req = GET("https://localhost:8080/motd").withCookies( PlainCookie("ID", "bG9zCg"), PlainCookie("Region", "SE-US") ) // Access and print all cookies req.cookies.foreach(println) // Get cookies by name val id: Option[PlainCookie] = req.getCookie("ID") val region: Option[PlainCookie] = req.getCookie("Region") // Get cookie values by name assert(req.getCookieValue("ID").contains("bG9zCg")) assert(req.getCookieValue("Region").contains("SE-US"))
Response Cookies
In HttpResponse, the cookies are a collection of Set-Cookie header values. Specialized access is provided by ResponseCookies, with each cookie represented as SetCookie.
import scamper.Implicits.stringToEntity import scamper.ResponseStatuses.Ok import scamper.cookies.{ ResponseCookies, SetCookie } // Build response with cookies val res = Ok("There is an answer.").withCookies( SetCookie("ID", "bG9zCg", path = Some("/motd"), secure = true), SetCookie("Region", "SE-US") ) // Access and print all cookies res.cookies.foreach(println) // Get cookies by name val id: Option[SetCookie] = res.getCookie("ID") val region: Option[SetCookie] = res.getCookie("Region") // Get attributes of ID cookie val path: String = id.flatMap(_.path).getOrElse("/") val secure: Boolean = id.map(_.secure).getOrElse(false) // Get cookie values by name assert(res.getCookieValue("ID").contains("bG9zCg")) assert(res.getCookieValue("Region").contains("SE-US"))
- package headers
Provides specialized access to message headers.
Provides specialized access to message headers.
Using Header Classes
Specialized header access is provided by type classes. Some headers are available to both requests and responses, and others are available only to a specific message type. This behavior is driven by the HTTP specification.
import scamper.Implicits.stringToUri import scamper.RequestMethods.GET import scamper.headers.{ Accept, Host } import scamper.types.Implicits.stringToMediaRange // Build request using 'Host' and 'Accept' headers val req = GET("/motd").withHost("localhost:8080").withAccept("text/plain") // Access and print header values printf("Host: %s%n", req.host) printf("Accept: %s%n", req.accept.head)
- package logging
Provides logging facilities.
- package server
Provides HTTP server implementation.
Provides HTTP server implementation.
Building HTTP Server
To build a server, you begin with
ServerApplication
. This is a mutable structure to which you apply changes to configure the server. Once the desired settings are applied, you invoke one of several methods to create the server.import java.io.File import scamper.BodyParsers import scamper.Implicits.{ stringToEntity, inputStreamToEntity } import scamper.ResponseStatuses.{ NotFound, Ok } import scamper.headers.TransferEncoding import scamper.server.HttpServer import scamper.server.Implicits.ServerHttpRequestType import scamper.types.Implicits.stringToTransferCoding // Get server application val app = HttpServer.app() // Add request handler to log all requests app.request { req => println(req.startLine) req } // Add request handler to specific request method and path app.get("/about") { req => Ok("This server is powered by Scamper.") } // Add request handler using path parameter app.put("/data/:id") { req => def update(id: Int, data: String): Boolean = ??? implicit val parser = BodyParsers.text() // Get path parameter val id = req.params.getInt("id") update(id, req.as[String]) match { case true => Ok() case false => NotFound() } } // Serve static files app.files("/main", new File("/path/to/public")) // Tell server to compress response app.response { res => res.withTransferEncoding("gzip", "chunked") } // Create server val server = app.create(8080) printf("Host: %s%n", server.host) printf("Port: %d%n", server.port) Thread.sleep(60 * 1000) // Close server when done server.close()
- package types
Defines standardized types for header classes in scamper.headers.
Defines standardized types for header classes in scamper.headers.
import scamper.Implicits.{ stringToEntity, stringToUri } import scamper.RequestMethods.GET import scamper.ResponseStatuses.Ok import scamper.headers.{ Accept, ContentType, TransferEncoding } import scamper.types.{ MediaRange, MediaType, TransferCoding } val json = MediaRange("application", "json", 0.9f) val html = MediaRange.parse("text/html; q=0.1") val req = GET("/motd").withAccept(json, html) val text = MediaType.parse("text/plain") val gzip = TransferCoding("gzip") val res = Ok("There is an answer.").withContentType(text).withTransferEncoding(gzip)
Using values defined in Implicits, properly formatted strings can be implicitly converted to standardized types.
import scamper.Implicits.{ stringToEntity, stringToUri } import scamper.RequestMethods.GET import scamper.ResponseStatuses.Ok import scamper.headers.{ Accept, ContentType, TransferEncoding } import scamper.types.Implicits._ val req = GET("/motd").withAccept("application/json; q=0.9", "text/html; q=0.1") val res = Ok("There is an answer.").withContentType("text/plain").withTransferEncoding("gzip")
Type Members
- trait BodyDecoding extends AnyRef
A mixin providing access to decoded message body.
- trait BodyParser[T] extends AnyRef
Provides utility for parsing HTTP message body.
Provides utility for parsing HTTP message body.
- See also
- trait Entity extends AnyRef
Representation of message body.
- case class EntityTooLarge(limit: Long) extends HttpException with Product with Serializable
Indicates entity exceeds established limit.
Indicates entity exceeds established limit.
EntityTooLarge
is a complement toReadLimitExceeded
. WhereasReadLimitExceeded
applies to the raw bytes of an input stream,EntityTooLarge
pertains to the entity itself, potentially subjected to decompression.- See also
- trait FilePart extends Part
Represents file content in multipart form data.
Represents file content in multipart form data.
- See also
- trait Header extends AnyRef
HTTP header
- case class HeaderNotFound(name: String) extends HttpException with Product with Serializable
Indicates absence of specified header.
- class HttpException extends RuntimeException
Indicates exception in HTTP processing.
- trait HttpMessage extends AnyRef
HTTP message
- trait HttpRequest extends HttpMessage with MessageBuilder[HttpRequest]
HTTP request
HTTP request
- See also
- trait HttpResponse extends HttpMessage with MessageBuilder[HttpResponse]
HTTP response
HTTP response
- See also
- trait HttpVersion extends AnyRef
HTTP version
- trait MessageBuilder[T <: HttpMessage] extends AnyRef
Provides builder pattern for HTTP message.
- trait Multipart extends AnyRef
Represents multipart form data.
Represents multipart form data.
- See also
- trait Part extends AnyRef
Represents part in multipart form data.
Represents part in multipart form data.
- See also
- trait QueryString extends AnyRef
Represents query string as mapped parameters.
- case class ReadLimitExceeded(limit: Long) extends IOException with Product with Serializable
Indicates attempt to read beyond an
InputStream
's established limit.Indicates attempt to read beyond an
InputStream
's established limit.ReadLimitExceeded
is a complement toEntityTooLarge
. WhereasReadLimitExceeded
applies to the raw bytes of an input stream,EntityTooLarge
pertains to the entity itself, potentially subjected to decompression.- See also
- trait RequestLine extends StartLine
HTTP request line
HTTP request line
- See also
- trait RequestMethod extends AnyRef
HTTP request method
HTTP request method
- See also
- trait ResponseStatus extends AnyRef
HTTP response status
HTTP response status
- See also
- trait StartLine extends AnyRef
HTTP message start line
- trait StatusLine extends StartLine
HTTP status line
HTTP status line
- See also
- trait TextPart extends Part
Represents text content in multipart form data.
Represents text content in multipart form data.
- See also
Value Members
- object BodyParsers
Includes default body parser implementations.
- object Entity
Provides factory for
Entity
. - object FilePart
Provides factory for FilePart.
- object Header
Provided for factory for
Header
. - object HttpRequest
HttpRequest factory
- object HttpResponse
HttpResponse factory
- object HttpVersion
HttpVersion factory
- object Implicits
Includes implicit converter functions.
- object Multipart
Provides factory for Multipart.
- object QueryString
Provides factory methods for QueryString.
- object RequestLine
RequestLine factory
- object RequestMethod
Provided factory for
RequestMethod
.Provided factory for
RequestMethod
.- See also
- object RequestMethods
Includes registered request methods.
- object ResponseStatus
Provided factory for
ResponseStatus
.Provided factory for
ResponseStatus
.- See also
- object ResponseStatuses
Includes standard HTTP statuses.
- object StatusLine
StatusLine factory
- object TextPart
Provides factory for TextPart.