|
| 1 | +# Getting Started |
| 2 | + |
| 3 | +This guide explains how to use `protocol-http` for building abstract HTTP interfaces. |
| 4 | + |
| 5 | +## Installation |
| 6 | + |
| 7 | +Add the gem to your project: |
| 8 | + |
| 9 | +~~~ bash |
| 10 | +$ bundle add protocol-http |
| 11 | +~~~ |
| 12 | + |
| 13 | +## Core Concepts |
| 14 | + |
| 15 | +`protocol-http` has several core concepts: |
| 16 | + |
| 17 | + - A {ruby Protocol::HTTP::Request} instance which represents an abstract HTTP request. Specific versions of HTTP may subclass this to track additional state. |
| 18 | + - A {ruby Protocol::HTTP::Response} instance which represents an abstract HTTP response. Specific versions of HTTP may subclass this to track additional state. |
| 19 | + - A {ruby Protocol::HTTP::Middleware} interface for building HTTP applications. |
| 20 | + - A {ruby Protocol::HTTP::Headers} interface for storing HTTP headers with semantics based on documented specifications (RFCs, etc). |
| 21 | + - A set of {ruby Protocol::HTTP::Body} classes which handle the internal request and response bodies, including bi-directional streaming. |
| 22 | + |
| 23 | +## Integration |
| 24 | + |
| 25 | +This gem does not provide any specific client or server implementation, rather it's used by several other gems. |
| 26 | + |
| 27 | + - [Protocol::HTTP1](https://github.com/socketry/protocol-http1) & [Protocol::HTTP2](https://github.com/socketry/protocol-http2) which provide client and server implementations. |
| 28 | + - [Async::HTTP](https://github.com/socketry/async-http) which provides connection pooling and concurrency. |
| 29 | + |
| 30 | +## Usage |
| 31 | + |
| 32 | +### Request |
| 33 | + |
| 34 | +{ruby Protocol::HTTP::Request} represents an HTTP request which can be used both server and client-side. |
| 35 | + |
| 36 | +``` ruby |
| 37 | +require 'protocol/http/request' |
| 38 | + |
| 39 | +# Short form (recommended): |
| 40 | +request = Protocol::HTTP::Request["GET", "/index.html", {"accept" => "text/html"}] |
| 41 | + |
| 42 | +# Long form: |
| 43 | +headers = Protocol::HTTP::Headers[["accept", "text/html"]] |
| 44 | +request = Protocol::HTTP::Request.new("http", "example.com", "GET", "/index.html", "HTTP/1.1", headers) |
| 45 | + |
| 46 | +# Access request properties |
| 47 | +request.method # => "GET" |
| 48 | +request.path # => "/index.html" |
| 49 | +request.headers # => Protocol::HTTP::Headers instance |
| 50 | +``` |
| 51 | + |
| 52 | +### Response |
| 53 | + |
| 54 | +{ruby Protocol::HTTP::Response} represents an HTTP response which can be used both server and client-side. |
| 55 | + |
| 56 | +``` ruby |
| 57 | +require 'protocol/http/response' |
| 58 | + |
| 59 | +# Short form (recommended): |
| 60 | +response = Protocol::HTTP::Response[200, {"content-type" => "text/html"}, "Hello, World!"] |
| 61 | + |
| 62 | +# Long form: |
| 63 | +headers = Protocol::HTTP::Headers["content-type" => "text/html"] |
| 64 | +body = Protocol::HTTP::Body::Buffered.wrap("Hello, World!") |
| 65 | +response = Protocol::HTTP::Response.new("HTTP/1.1", 200, headers, body) |
| 66 | + |
| 67 | +# Access response properties |
| 68 | +response.status # => 200 |
| 69 | +response.headers # => Protocol::HTTP::Headers instance |
| 70 | +response.body # => Body instance |
| 71 | + |
| 72 | +# Status checking methods |
| 73 | +response.success? # => true (200-299) |
| 74 | +response.ok? # => true (200) |
| 75 | +response.redirection? # => false (300-399) |
| 76 | +response.failure? # => false (400-599) |
| 77 | +``` |
| 78 | + |
| 79 | +### Headers |
| 80 | + |
| 81 | +{ruby Protocol::HTTP::Headers} provides semantically meaningful interpretation of header values and implements case-normalising keys. |
| 82 | + |
| 83 | +#### Basic Usage |
| 84 | + |
| 85 | +``` ruby |
| 86 | +require 'protocol/http/headers' |
| 87 | + |
| 88 | +headers = Protocol::HTTP::Headers.new |
| 89 | + |
| 90 | +# Assignment by title-case key: |
| 91 | +headers['Content-Type'] = "image/jpeg" |
| 92 | + |
| 93 | +# Lookup by lower-case (normalized) key: |
| 94 | +headers['content-type'] |
| 95 | +# => "image/jpeg" |
| 96 | +``` |
| 97 | + |
| 98 | +#### Semantic Processing |
| 99 | + |
| 100 | +Many headers receive special semantic processing, automatically splitting comma-separated values and providing structured access: |
| 101 | + |
| 102 | +``` ruby |
| 103 | +# Accept header with quality values: |
| 104 | +headers['Accept'] = 'text/html, application/json;q=0.8, */*;q=0.1' |
| 105 | +accept = headers['accept'] |
| 106 | +# => ["text/html", "application/json;q=0.8", "*/*;q=0.1"] |
| 107 | + |
| 108 | +# Access parsed media ranges with quality factors: |
| 109 | +accept.media_ranges.each do |range| |
| 110 | + puts "#{range.type}/#{range.subtype} (q=#{range.quality_factor})" |
| 111 | +end |
| 112 | +# text/html (q=1.0) |
| 113 | +# application/json (q=0.8) |
| 114 | +# */* (q=0.1) |
| 115 | + |
| 116 | +# Accept-Encoding automatically splits values: |
| 117 | +headers['Accept-Encoding'] = 'gzip, deflate, br;q=0.9' |
| 118 | +headers['accept-encoding'] |
| 119 | +# => ["gzip", "deflate", "br;q=0.9"] |
| 120 | + |
| 121 | +# Cache-Control splits directives: |
| 122 | +headers['Cache-Control'] = 'max-age=3600, no-cache, must-revalidate' |
| 123 | +headers['cache-control'] |
| 124 | +# => ["max-age=3600", "no-cache", "must-revalidate"] |
| 125 | + |
| 126 | +# Vary header normalizes field names to lowercase: |
| 127 | +headers['Vary'] = 'Accept-Encoding, User-Agent' |
| 128 | +headers['vary'] |
| 129 | +# => ["accept-encoding", "user-agent"] |
| 130 | +``` |
0 commit comments