protocol-http 0.51.1 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/design-overview.md +209 -0
- data/context/getting-started.md +130 -0
- data/context/hypertext-references.md +140 -0
- data/context/index.yaml +36 -0
- data/context/message-body.md +330 -0
- data/context/middleware.md +195 -0
- data/context/streaming.md +132 -0
- data/context/url-parsing.md +130 -0
- data/lib/protocol/http/accept_encoding.rb +1 -1
- data/lib/protocol/http/body/buffered.rb +4 -2
- data/lib/protocol/http/body/completable.rb +18 -1
- data/lib/protocol/http/body/deflate.rb +13 -2
- data/lib/protocol/http/body/digestable.rb +12 -2
- data/lib/protocol/http/body/file.rb +6 -2
- data/lib/protocol/http/body/head.rb +7 -0
- data/lib/protocol/http/body/inflate.rb +2 -2
- data/lib/protocol/http/body/rewindable.rb +11 -1
- data/lib/protocol/http/body/stream.rb +17 -2
- data/lib/protocol/http/body/streamable.rb +18 -2
- data/lib/protocol/http/body/writable.rb +3 -3
- data/lib/protocol/http/error.rb +1 -1
- data/lib/protocol/http/header/cache_control.rb +1 -1
- data/lib/protocol/http/headers.rb +16 -6
- data/lib/protocol/http/reference.rb +29 -15
- data/lib/protocol/http/request.rb +1 -1
- data/lib/protocol/http/response.rb +2 -1
- data/lib/protocol/http/version.rb +1 -1
- data/readme.md +21 -2
- data/releases.md +11 -0
- data.tar.gz.sig +0 -0
- metadata +9 -1
- metadata.gz.sig +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49ea23b4e99ab120bfd68806628631db474b789d7ca841a28877ba176239816f
|
4
|
+
data.tar.gz: 2dbe6a91006f94a303babb3b156a55eb82ac60025f111d7f003896e744009394
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d05dca30d34d5a66483cf09b3b3927af1bd6cf4679cd2421fa0737ab7f6cd56b61fd681d90c7b3252c6bc79d6843b8460c3c74c595b31ff636fb46920088165
|
7
|
+
data.tar.gz: 75cb7b599a2ba9d49f859b16f00f94bc2e59118813779e9a4a7e5804ea94d6cc66cc361fc4ef0d7ce957e26bbf7a6a53e1f7b02ec92e438260bfa5a1a33e1299
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -0,0 +1,209 @@
|
|
1
|
+
# Design Overview
|
2
|
+
|
3
|
+
This guide explains the high level design of `protocol-http` in the context of wider design patterns that can be used to implement HTTP clients and servers.
|
4
|
+
|
5
|
+
## Request/Response Model
|
6
|
+
|
7
|
+
The main model we support is the request/response model. A client sends a request to a server which return response. The protocol is responsible for serializing the request and response objects.
|
8
|
+
|
9
|
+
```mermaid
|
10
|
+
sequenceDiagram
|
11
|
+
participant CA as Application
|
12
|
+
participant Client
|
13
|
+
participant Server
|
14
|
+
participant SA as Application
|
15
|
+
CA->>+Client: Request
|
16
|
+
Client->>+Server: Request
|
17
|
+
Server->>+SA: Request
|
18
|
+
SA->>+Server: Response
|
19
|
+
Server->>+Client: Response
|
20
|
+
Client->>+CA: Response
|
21
|
+
```
|
22
|
+
|
23
|
+
We provide an interface for request and response objects. This provides performance, predictability and robustness. This model has proven itself over several years, handling a variety of different use cases.
|
24
|
+
|
25
|
+
~~~ ruby
|
26
|
+
class Request
|
27
|
+
attr :method
|
28
|
+
attr :target
|
29
|
+
attr :headers
|
30
|
+
attr :body
|
31
|
+
end
|
32
|
+
|
33
|
+
class Response
|
34
|
+
attr :status
|
35
|
+
attr :headers
|
36
|
+
attr :body
|
37
|
+
end
|
38
|
+
~~~
|
39
|
+
|
40
|
+
One other advantage is that it's symmetrical between clients and servers with a clear mapping, i.e. the protocol is responsible for transiting requests from the client to the server, and responses from the server back to the client. This helps us separate and define request/response interfaces independently from protocol implementation.
|
41
|
+
|
42
|
+
### Client Design
|
43
|
+
|
44
|
+
A request/response model implies that you create a request and receive a response back. This maps to a normal function call where the request is the argument and the response is the returned value.
|
45
|
+
|
46
|
+
~~~ ruby
|
47
|
+
request = Request.new("GET", url)
|
48
|
+
response = client.call(request)
|
49
|
+
|
50
|
+
response.headers
|
51
|
+
response.read
|
52
|
+
~~~
|
53
|
+
|
54
|
+
## Stream Model
|
55
|
+
|
56
|
+
An alternative model is the stream model. This model is more suitable for WebSockets and other persistent bi-directional channels.
|
57
|
+
|
58
|
+
```mermaid
|
59
|
+
sequenceDiagram
|
60
|
+
participant CA as Application
|
61
|
+
participant Client
|
62
|
+
participant Server
|
63
|
+
participant SA as Application
|
64
|
+
CA->>+Client: Stream
|
65
|
+
Client->>+Server: Stream
|
66
|
+
Server->>+SA: Stream
|
67
|
+
```
|
68
|
+
|
69
|
+
The interfaces for streaming can be implemented a bit differently, since a response is not returned but rather assigned to the stream, and the streaming occurs in the same execution context as the client or server handling the request.
|
70
|
+
|
71
|
+
~~~ ruby
|
72
|
+
class Stream
|
73
|
+
# Request details.
|
74
|
+
attr :method
|
75
|
+
attr :target
|
76
|
+
attr :headers
|
77
|
+
|
78
|
+
attr :response
|
79
|
+
|
80
|
+
# Write the response and start streaming the output body.
|
81
|
+
def respond(status, headers)
|
82
|
+
response.status = status
|
83
|
+
response.headers = headers
|
84
|
+
end
|
85
|
+
|
86
|
+
# Request body.
|
87
|
+
attr_accessor :input
|
88
|
+
|
89
|
+
# Response body.
|
90
|
+
attr_accessor :output
|
91
|
+
|
92
|
+
# Write to the response body.
|
93
|
+
def write(...)
|
94
|
+
@output.write(...)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Read from the request body.
|
98
|
+
def read
|
99
|
+
@input.read
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class Response
|
104
|
+
def initialize(method, target)
|
105
|
+
@input = Body::Writable.new
|
106
|
+
@output = Body::Writable.new
|
107
|
+
end
|
108
|
+
|
109
|
+
attr_accessor :status
|
110
|
+
attr_accessor :headers
|
111
|
+
|
112
|
+
# Prepare a stream for making a request.
|
113
|
+
def request(method, target, headers)
|
114
|
+
# Create a request stream suitable for writing into the buffered response:
|
115
|
+
Stream.new(method, target, headers, self, @input, @output)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Write to the request body.
|
119
|
+
def write(...)
|
120
|
+
@input.write(...)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Read from the response body.
|
124
|
+
def read
|
125
|
+
@output.read
|
126
|
+
end
|
127
|
+
end
|
128
|
+
~~~
|
129
|
+
|
130
|
+
### Client Design
|
131
|
+
|
132
|
+
A stream model implies that you create a stream which contains both the request and response bodies. This maps to a normal function call where the argument is the stream and the returned value is ignored.
|
133
|
+
|
134
|
+
~~~ ruby
|
135
|
+
response = Response.new
|
136
|
+
stream = response.request("GET", url)
|
137
|
+
|
138
|
+
client.call(stream)
|
139
|
+
|
140
|
+
response.headers
|
141
|
+
response.read
|
142
|
+
~~~
|
143
|
+
|
144
|
+
## Differences
|
145
|
+
|
146
|
+
The request/response model has a symmetrical design which naturally uses the return value for the result of executing the request. The result encapsulates the behaviour of how to read the response status, headers and body. Because of that, streaming input and output becomes a function of the result object itself. As in:
|
147
|
+
|
148
|
+
~~~ ruby
|
149
|
+
def call(request)
|
150
|
+
body = Body::Writable.new
|
151
|
+
|
152
|
+
Fiber.schedule do
|
153
|
+
while chunk = request.input.read
|
154
|
+
body.write(chunk.reverse)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
return Response[200, headers, body]
|
159
|
+
end
|
160
|
+
|
161
|
+
input = Body::Writable.new
|
162
|
+
response = call(... body ...)
|
163
|
+
|
164
|
+
input.write("Hello World")
|
165
|
+
input.close
|
166
|
+
response.read -> "dlroW olleH"
|
167
|
+
~~~
|
168
|
+
|
169
|
+
The streaming model does not have the same symmetry, and instead opts for a uni-directional flow of information.
|
170
|
+
|
171
|
+
~~~ruby
|
172
|
+
def call(stream)
|
173
|
+
stream.respond(200, headers)
|
174
|
+
|
175
|
+
Fiber.schedule do
|
176
|
+
while chunk = stream.read
|
177
|
+
stream.write(chunk.reverse)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
input = Body::Writable.new
|
183
|
+
response = Response.new(...input...)
|
184
|
+
call(response.stream)
|
185
|
+
|
186
|
+
input.write("Hello World")
|
187
|
+
input.close
|
188
|
+
response.read -> "dlroW olleH"
|
189
|
+
~~~
|
190
|
+
|
191
|
+
The value of this uni-directional flow is that it is natural for the stream to be taken out of the scope imposed by the nested `call(request)` model. However, the user must explicitly close the stream, since it's no longer scoped to the client and/or server.
|
192
|
+
|
193
|
+
## Interim Response Handling
|
194
|
+
|
195
|
+
Interim responses are responses that are sent before the final response. They are used for things like `103 Early Hints` and `100 Continue`. These responses are sent before the final response, and are used to signal to the client that the server is still processing the request.
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
body = Body::Writable.new
|
199
|
+
|
200
|
+
interim_response_callback = proc do |status, headers|
|
201
|
+
if status == 100
|
202
|
+
# Continue sending the request body.
|
203
|
+
body.write("Hello World")
|
204
|
+
body.close
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
response = client.post("/upload", {'expect' => '100-continue'}, body, interim_response: interim_response_callback)
|
209
|
+
```
|
@@ -0,0 +1,130 @@
|
|
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,0 +1,140 @@
|
|
1
|
+
# Hypertext References
|
2
|
+
|
3
|
+
This guide explains how to use `Protocol::HTTP::Reference` for constructing and manipulating hypertext references (URLs with parameters).
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
{ruby Protocol::HTTP::Reference} is used to construct "hypertext references" which consist of a path and URL-encoded parameters. References provide a rich API for URL construction, path manipulation, and parameter handling.
|
8
|
+
|
9
|
+
## Basic Construction
|
10
|
+
|
11
|
+
``` ruby
|
12
|
+
require 'protocol/http/reference'
|
13
|
+
|
14
|
+
# Simple reference with parameters:
|
15
|
+
reference = Protocol::HTTP::Reference.new("/search", nil, nil, {q: 'kittens', limit: 10})
|
16
|
+
reference.to_s
|
17
|
+
# => "/search?q=kittens&limit=10"
|
18
|
+
|
19
|
+
# Parse existing URLs:
|
20
|
+
reference = Protocol::HTTP::Reference.parse("/api/users?page=2&sort=name#results")
|
21
|
+
reference.path # => "/api/users"
|
22
|
+
reference.query # => "page=2&sort=name"
|
23
|
+
reference.fragment # => "results"
|
24
|
+
|
25
|
+
# To get parameters as a hash, decode the query string:
|
26
|
+
parameters = Protocol::HTTP::URL.decode(reference.query)
|
27
|
+
parameters # => {"page" => "2", "sort" => "name"}
|
28
|
+
```
|
29
|
+
|
30
|
+
## Path Manipulation
|
31
|
+
|
32
|
+
References support sophisticated path manipulation including relative path resolution:
|
33
|
+
|
34
|
+
``` ruby
|
35
|
+
base = Protocol::HTTP::Reference.new("/api/v1/users")
|
36
|
+
|
37
|
+
# Append paths:
|
38
|
+
user_detail = base.with(path: "123")
|
39
|
+
user_detail.to_s # => "/api/v1/users/123"
|
40
|
+
|
41
|
+
# Relative path navigation:
|
42
|
+
parent = user_detail.with(path: "../groups", pop: true)
|
43
|
+
parent.to_s # => "/api/v1/groups"
|
44
|
+
|
45
|
+
# Absolute path replacement:
|
46
|
+
root = user_detail.with(path: "/status")
|
47
|
+
root.to_s # => "/status"
|
48
|
+
```
|
49
|
+
|
50
|
+
## Advanced Parameter Handling
|
51
|
+
|
52
|
+
``` ruby
|
53
|
+
# Complex parameter structures:
|
54
|
+
reference = Protocol::HTTP::Reference.new("/search", nil, nil, {
|
55
|
+
filters: {
|
56
|
+
category: "books",
|
57
|
+
price: {min: 10, max: 50}
|
58
|
+
},
|
59
|
+
tags: ["fiction", "mystery"]
|
60
|
+
})
|
61
|
+
|
62
|
+
reference.to_s
|
63
|
+
# => "/search?filters[category]=books&filters[price][min]=10&filters[price][max]=50&tags[]=fiction&tags[]=mystery"
|
64
|
+
|
65
|
+
# Parameter merging:
|
66
|
+
base = Protocol::HTTP::Reference.new("/api", nil, nil, {version: "v1", format: "json"})
|
67
|
+
extended = base.with(parameters: {detailed: true}, merge: true)
|
68
|
+
extended.to_s
|
69
|
+
# => "/api?version=v1&format=json&detailed=true"
|
70
|
+
|
71
|
+
# Parameter replacement (using merge: false):
|
72
|
+
replaced = base.with(parameters: {format: "xml"}, merge: false)
|
73
|
+
replaced.to_s
|
74
|
+
# => "/api?format=xml"
|
75
|
+
```
|
76
|
+
|
77
|
+
## Merge Behavior and Query Strings
|
78
|
+
|
79
|
+
The `merge` parameter controls both parameter handling and query string behavior:
|
80
|
+
|
81
|
+
``` ruby
|
82
|
+
# Create a reference with both query string and parameters:
|
83
|
+
ref = Protocol::HTTP::Reference.new("/api", "existing=query", nil, {version: "v1"})
|
84
|
+
ref.to_s
|
85
|
+
# => "/api?existing=query&version=v1"
|
86
|
+
|
87
|
+
# merge: true (default) - keeps existing query string:
|
88
|
+
merged = ref.with(parameters: {new: "argument"}, merge: true)
|
89
|
+
merged.to_s
|
90
|
+
# => "/api?existing=query&version=v1&new=argument"
|
91
|
+
|
92
|
+
# merge: false with new parameters - clears query string:
|
93
|
+
replaced = ref.with(parameters: {new: "argument"}, merge: false)
|
94
|
+
replaced.to_s
|
95
|
+
# => "/api?new=argument"
|
96
|
+
|
97
|
+
# merge: false without new parameters - keeps everything:
|
98
|
+
unchanged = ref.with(path: "v2", merge: false)
|
99
|
+
unchanged.to_s
|
100
|
+
# => "/api/v2?existing=query&version=v1"
|
101
|
+
```
|
102
|
+
|
103
|
+
## URL Encoding and Special Characters
|
104
|
+
|
105
|
+
References handle URL encoding automatically:
|
106
|
+
|
107
|
+
``` ruby
|
108
|
+
# Spaces and special characters:
|
109
|
+
reference = Protocol::HTTP::Reference.new("/search", nil, nil, {
|
110
|
+
q: "hello world",
|
111
|
+
filter: "price > $10"
|
112
|
+
})
|
113
|
+
reference.to_s
|
114
|
+
# => "/search?q=hello%20world&filter=price%20%3E%20%2410"
|
115
|
+
|
116
|
+
# Unicode support:
|
117
|
+
unicode_ref = Protocol::HTTP::Reference.new("/files", nil, nil, {
|
118
|
+
name: "résumé.pdf",
|
119
|
+
emoji: "😀"
|
120
|
+
})
|
121
|
+
unicode_ref.to_s
|
122
|
+
# => "/files?name=r%C3%A9sum%C3%A9.pdf&emoji=%F0%9F%98%80"
|
123
|
+
```
|
124
|
+
|
125
|
+
## Reference Merging
|
126
|
+
|
127
|
+
References can be merged following RFC2396 URI resolution rules:
|
128
|
+
|
129
|
+
``` ruby
|
130
|
+
base = Protocol::HTTP::Reference.new("/docs/guide/")
|
131
|
+
relative = Protocol::HTTP::Reference.new("../api/reference.html")
|
132
|
+
|
133
|
+
merged = base + relative
|
134
|
+
merged.to_s # => "/docs/api/reference.html"
|
135
|
+
|
136
|
+
# Absolute references override completely
|
137
|
+
absolute = Protocol::HTTP::Reference.new("/completely/different/path")
|
138
|
+
result = base + absolute
|
139
|
+
result.to_s # => "/completely/different/path"
|
140
|
+
```
|
data/context/index.yaml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Automatically generated context index for Utopia::Project guides.
|
2
|
+
# Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
|
3
|
+
---
|
4
|
+
description: Provides abstractions to handle HTTP protocols.
|
5
|
+
metadata:
|
6
|
+
documentation_uri: https://socketry.github.io/protocol-http/
|
7
|
+
source_code_uri: https://github.com/socketry/protocol-http.git
|
8
|
+
files:
|
9
|
+
- path: getting-started.md
|
10
|
+
title: Getting Started
|
11
|
+
description: This guide explains how to use `protocol-http` for building abstract
|
12
|
+
HTTP interfaces.
|
13
|
+
- path: message-body.md
|
14
|
+
title: Message Body
|
15
|
+
description: This guide explains how to work with HTTP request and response message
|
16
|
+
bodies using `Protocol::HTTP::Body` classes.
|
17
|
+
- path: middleware.md
|
18
|
+
title: Middleware
|
19
|
+
description: This guide explains how to build and use HTTP middleware with `Protocol::HTTP::Middleware`.
|
20
|
+
- path: hypertext-references.md
|
21
|
+
title: Hypertext References
|
22
|
+
description: This guide explains how to use `Protocol::HTTP::Reference` for constructing
|
23
|
+
and manipulating hypertext references (URLs with parameters).
|
24
|
+
- path: url-parsing.md
|
25
|
+
title: URL Parsing
|
26
|
+
description: This guide explains how to use `Protocol::HTTP::URL` for parsing and
|
27
|
+
manipulating URL components, particularly query strings and parameters.
|
28
|
+
- path: streaming.md
|
29
|
+
title: Streaming
|
30
|
+
description: This guide gives an overview of how to implement streaming requests
|
31
|
+
and responses.
|
32
|
+
- path: design-overview.md
|
33
|
+
title: Design Overview
|
34
|
+
description: This guide explains the high level design of `protocol-http` in the
|
35
|
+
context of wider design patterns that can be used to implement HTTP clients and
|
36
|
+
servers.
|