cannam@147
|
1 // Copyright (c) 2017 Sandstorm Development Group, Inc. and contributors
|
cannam@147
|
2 // Licensed under the MIT License:
|
cannam@147
|
3 //
|
cannam@147
|
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
|
cannam@147
|
5 // of this software and associated documentation files (the "Software"), to deal
|
cannam@147
|
6 // in the Software without restriction, including without limitation the rights
|
cannam@147
|
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
cannam@147
|
8 // copies of the Software, and to permit persons to whom the Software is
|
cannam@147
|
9 // furnished to do so, subject to the following conditions:
|
cannam@147
|
10 //
|
cannam@147
|
11 // The above copyright notice and this permission notice shall be included in
|
cannam@147
|
12 // all copies or substantial portions of the Software.
|
cannam@147
|
13 //
|
cannam@147
|
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
cannam@147
|
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
cannam@147
|
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
cannam@147
|
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
cannam@147
|
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
cannam@147
|
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
cannam@147
|
20 // THE SOFTWARE.
|
cannam@147
|
21
|
cannam@147
|
22 #ifndef KJ_COMPAT_HTTP_H_
|
cannam@147
|
23 #define KJ_COMPAT_HTTP_H_
|
cannam@147
|
24 // The KJ HTTP client/server library.
|
cannam@147
|
25 //
|
cannam@147
|
26 // This is a simple library which can be used to implement an HTTP client or server. Properties
|
cannam@147
|
27 // of this library include:
|
cannam@147
|
28 // - Uses KJ async framework.
|
cannam@147
|
29 // - Agnostic to transport layer -- you can provide your own.
|
cannam@147
|
30 // - Header parsing is zero-copy -- it results in strings that point directly into the buffer
|
cannam@147
|
31 // received off the wire.
|
cannam@147
|
32 // - Application code which reads and writes headers refers to headers by symbolic names, not by
|
cannam@147
|
33 // string literals, with lookups being array-index-based, not map-based. To make this possible,
|
cannam@147
|
34 // the application announces what headers it cares about in advance, in order to assign numeric
|
cannam@147
|
35 // values to them.
|
cannam@147
|
36 // - Methods are identified by an enum.
|
cannam@147
|
37
|
cannam@147
|
38 #include <kj/string.h>
|
cannam@147
|
39 #include <kj/vector.h>
|
cannam@147
|
40 #include <kj/memory.h>
|
cannam@147
|
41 #include <kj/one-of.h>
|
cannam@147
|
42 #include <kj/async-io.h>
|
cannam@147
|
43
|
cannam@147
|
44 namespace kj {
|
cannam@147
|
45
|
cannam@147
|
46 #define KJ_HTTP_FOR_EACH_METHOD(MACRO) \
|
cannam@147
|
47 MACRO(GET) \
|
cannam@147
|
48 MACRO(HEAD) \
|
cannam@147
|
49 MACRO(POST) \
|
cannam@147
|
50 MACRO(PUT) \
|
cannam@147
|
51 MACRO(DELETE) \
|
cannam@147
|
52 MACRO(PATCH) \
|
cannam@147
|
53 MACRO(PURGE) \
|
cannam@147
|
54 MACRO(OPTIONS) \
|
cannam@147
|
55 MACRO(TRACE) \
|
cannam@147
|
56 /* standard methods */ \
|
cannam@147
|
57 /* */ \
|
cannam@147
|
58 /* (CONNECT is intentionally omitted since it is handled specially in HttpHandler) */ \
|
cannam@147
|
59 \
|
cannam@147
|
60 MACRO(COPY) \
|
cannam@147
|
61 MACRO(LOCK) \
|
cannam@147
|
62 MACRO(MKCOL) \
|
cannam@147
|
63 MACRO(MOVE) \
|
cannam@147
|
64 MACRO(PROPFIND) \
|
cannam@147
|
65 MACRO(PROPPATCH) \
|
cannam@147
|
66 MACRO(SEARCH) \
|
cannam@147
|
67 MACRO(UNLOCK) \
|
cannam@147
|
68 /* WebDAV */ \
|
cannam@147
|
69 \
|
cannam@147
|
70 MACRO(REPORT) \
|
cannam@147
|
71 MACRO(MKACTIVITY) \
|
cannam@147
|
72 MACRO(CHECKOUT) \
|
cannam@147
|
73 MACRO(MERGE) \
|
cannam@147
|
74 /* Subversion */ \
|
cannam@147
|
75 \
|
cannam@147
|
76 MACRO(MSEARCH) \
|
cannam@147
|
77 MACRO(NOTIFY) \
|
cannam@147
|
78 MACRO(SUBSCRIBE) \
|
cannam@147
|
79 MACRO(UNSUBSCRIBE)
|
cannam@147
|
80 /* UPnP */
|
cannam@147
|
81
|
cannam@147
|
82 #define KJ_HTTP_FOR_EACH_CONNECTION_HEADER(MACRO) \
|
cannam@147
|
83 MACRO(connection, "Connection") \
|
cannam@147
|
84 MACRO(contentLength, "Content-Length") \
|
cannam@147
|
85 MACRO(keepAlive, "Keep-Alive") \
|
cannam@147
|
86 MACRO(te, "TE") \
|
cannam@147
|
87 MACRO(trailer, "Trailer") \
|
cannam@147
|
88 MACRO(transferEncoding, "Transfer-Encoding") \
|
cannam@147
|
89 MACRO(upgrade, "Upgrade")
|
cannam@147
|
90
|
cannam@147
|
91 enum class HttpMethod {
|
cannam@147
|
92 // Enum of known HTTP methods.
|
cannam@147
|
93 //
|
cannam@147
|
94 // We use an enum rather than a string to allow for faster parsing and switching and to reduce
|
cannam@147
|
95 // ambiguity.
|
cannam@147
|
96
|
cannam@147
|
97 #define DECLARE_METHOD(id) id,
|
cannam@147
|
98 KJ_HTTP_FOR_EACH_METHOD(DECLARE_METHOD)
|
cannam@147
|
99 #undef DECALRE_METHOD
|
cannam@147
|
100 };
|
cannam@147
|
101
|
cannam@147
|
102 kj::StringPtr KJ_STRINGIFY(HttpMethod method);
|
cannam@147
|
103 kj::Maybe<HttpMethod> tryParseHttpMethod(kj::StringPtr name);
|
cannam@147
|
104
|
cannam@147
|
105 class HttpHeaderTable;
|
cannam@147
|
106
|
cannam@147
|
107 class HttpHeaderId {
|
cannam@147
|
108 // Identifies an HTTP header by numeric ID that indexes into an HttpHeaderTable.
|
cannam@147
|
109 //
|
cannam@147
|
110 // The KJ HTTP API prefers that headers be identified by these IDs for a few reasons:
|
cannam@147
|
111 // - Integer lookups are much more efficient than string lookups.
|
cannam@147
|
112 // - Case-insensitivity is awkward to deal with when const strings are being passed to the lookup
|
cannam@147
|
113 // method.
|
cannam@147
|
114 // - Writing out strings less often means fewer typos.
|
cannam@147
|
115 //
|
cannam@147
|
116 // See HttpHeaderTable for usage hints.
|
cannam@147
|
117
|
cannam@147
|
118 public:
|
cannam@147
|
119 HttpHeaderId() = default;
|
cannam@147
|
120
|
cannam@147
|
121 inline bool operator==(const HttpHeaderId& other) const { return id == other.id; }
|
cannam@147
|
122 inline bool operator!=(const HttpHeaderId& other) const { return id != other.id; }
|
cannam@147
|
123 inline bool operator< (const HttpHeaderId& other) const { return id < other.id; }
|
cannam@147
|
124 inline bool operator> (const HttpHeaderId& other) const { return id > other.id; }
|
cannam@147
|
125 inline bool operator<=(const HttpHeaderId& other) const { return id <= other.id; }
|
cannam@147
|
126 inline bool operator>=(const HttpHeaderId& other) const { return id >= other.id; }
|
cannam@147
|
127
|
cannam@147
|
128 inline size_t hashCode() const { return id; }
|
cannam@147
|
129
|
cannam@147
|
130 kj::StringPtr toString() const;
|
cannam@147
|
131
|
cannam@147
|
132 void requireFrom(HttpHeaderTable& table) const;
|
cannam@147
|
133 // In debug mode, throws an exception if the HttpHeaderId is not from the given table.
|
cannam@147
|
134 //
|
cannam@147
|
135 // In opt mode, no-op.
|
cannam@147
|
136
|
cannam@147
|
137 #define KJ_HTTP_FOR_EACH_BUILTIN_HEADER(MACRO) \
|
cannam@147
|
138 MACRO(HOST, "Host") \
|
cannam@147
|
139 MACRO(DATE, "Date") \
|
cannam@147
|
140 MACRO(LOCATION, "Location") \
|
cannam@147
|
141 MACRO(CONTENT_TYPE, "Content-Type")
|
cannam@147
|
142 // For convenience, these very-common headers are valid for all HttpHeaderTables. You can refer
|
cannam@147
|
143 // to them like:
|
cannam@147
|
144 //
|
cannam@147
|
145 // HttpHeaderId::HOST
|
cannam@147
|
146 //
|
cannam@147
|
147 // TODO(0.7): Fill this out with more common headers.
|
cannam@147
|
148
|
cannam@147
|
149 #define DECLARE_HEADER(id, name) \
|
cannam@147
|
150 static const HttpHeaderId id;
|
cannam@147
|
151 // Declare a constant for each builtin header, e.g.: HttpHeaderId::CONNECTION
|
cannam@147
|
152
|
cannam@147
|
153 KJ_HTTP_FOR_EACH_BUILTIN_HEADER(DECLARE_HEADER);
|
cannam@147
|
154 #undef DECLARE_HEADER
|
cannam@147
|
155
|
cannam@147
|
156 private:
|
cannam@147
|
157 HttpHeaderTable* table;
|
cannam@147
|
158 uint id;
|
cannam@147
|
159
|
cannam@147
|
160 inline explicit constexpr HttpHeaderId(HttpHeaderTable* table, uint id): table(table), id(id) {}
|
cannam@147
|
161 friend class HttpHeaderTable;
|
cannam@147
|
162 friend class HttpHeaders;
|
cannam@147
|
163 };
|
cannam@147
|
164
|
cannam@147
|
165 class HttpHeaderTable {
|
cannam@147
|
166 // Construct an HttpHeaderTable to declare which headers you'll be interested in later on, and
|
cannam@147
|
167 // to manufacture IDs for them.
|
cannam@147
|
168 //
|
cannam@147
|
169 // Example:
|
cannam@147
|
170 //
|
cannam@147
|
171 // // Build a header table with the headers we are interested in.
|
cannam@147
|
172 // kj::HttpHeaderTable::Builder builder;
|
cannam@147
|
173 // const HttpHeaderId accept = builder.add("Accept");
|
cannam@147
|
174 // const HttpHeaderId contentType = builder.add("Content-Type");
|
cannam@147
|
175 // kj::HttpHeaderTable table(kj::mv(builder));
|
cannam@147
|
176 //
|
cannam@147
|
177 // // Create an HTTP client.
|
cannam@147
|
178 // auto client = kj::newHttpClient(table, network);
|
cannam@147
|
179 //
|
cannam@147
|
180 // // Get http://example.com.
|
cannam@147
|
181 // HttpHeaders headers(table);
|
cannam@147
|
182 // headers.set(accept, "text/html");
|
cannam@147
|
183 // auto response = client->send(kj::HttpMethod::GET, "http://example.com", headers)
|
cannam@147
|
184 // .wait(waitScope);
|
cannam@147
|
185 // auto msg = kj::str("Response content type: ", response.headers.get(contentType));
|
cannam@147
|
186
|
cannam@147
|
187 struct IdsByNameMap;
|
cannam@147
|
188
|
cannam@147
|
189 public:
|
cannam@147
|
190 HttpHeaderTable();
|
cannam@147
|
191 // Constructs a table that only contains the builtin headers.
|
cannam@147
|
192
|
cannam@147
|
193 class Builder {
|
cannam@147
|
194 public:
|
cannam@147
|
195 Builder();
|
cannam@147
|
196 HttpHeaderId add(kj::StringPtr name);
|
cannam@147
|
197 Own<HttpHeaderTable> build();
|
cannam@147
|
198
|
cannam@147
|
199 HttpHeaderTable& getFutureTable();
|
cannam@147
|
200 // Get the still-unbuilt header table. You cannot actually use it until build() has been
|
cannam@147
|
201 // called.
|
cannam@147
|
202 //
|
cannam@147
|
203 // This method exists to help when building a shared header table -- the Builder may be passed
|
cannam@147
|
204 // to several components, each of which will register the headers they need and get a reference
|
cannam@147
|
205 // to the future table.
|
cannam@147
|
206
|
cannam@147
|
207 private:
|
cannam@147
|
208 kj::Own<HttpHeaderTable> table;
|
cannam@147
|
209 };
|
cannam@147
|
210
|
cannam@147
|
211 KJ_DISALLOW_COPY(HttpHeaderTable); // Can't copy because HttpHeaderId points to the table.
|
cannam@147
|
212 ~HttpHeaderTable() noexcept(false);
|
cannam@147
|
213
|
cannam@147
|
214 uint idCount();
|
cannam@147
|
215 // Return the number of IDs in the table.
|
cannam@147
|
216
|
cannam@147
|
217 kj::Maybe<HttpHeaderId> stringToId(kj::StringPtr name);
|
cannam@147
|
218 // Try to find an ID for the given name. The matching is case-insensitive, per the HTTP spec.
|
cannam@147
|
219 //
|
cannam@147
|
220 // Note: if `name` contains characters that aren't allowed in HTTP header names, this may return
|
cannam@147
|
221 // a bogus value rather than null, due to optimizations used in case-insensitive matching.
|
cannam@147
|
222
|
cannam@147
|
223 kj::StringPtr idToString(HttpHeaderId id);
|
cannam@147
|
224 // Get the canonical string name for the given ID.
|
cannam@147
|
225
|
cannam@147
|
226 private:
|
cannam@147
|
227 kj::Vector<kj::StringPtr> namesById;
|
cannam@147
|
228 kj::Own<IdsByNameMap> idsByName;
|
cannam@147
|
229 };
|
cannam@147
|
230
|
cannam@147
|
231 class HttpHeaders {
|
cannam@147
|
232 // Represents a set of HTTP headers.
|
cannam@147
|
233 //
|
cannam@147
|
234 // This class guards against basic HTTP header injection attacks: Trying to set a header name or
|
cannam@147
|
235 // value containing a newline, carriage return, or other invalid character will throw an
|
cannam@147
|
236 // exception.
|
cannam@147
|
237
|
cannam@147
|
238 public:
|
cannam@147
|
239 explicit HttpHeaders(HttpHeaderTable& table);
|
cannam@147
|
240
|
cannam@147
|
241 KJ_DISALLOW_COPY(HttpHeaders);
|
cannam@147
|
242 HttpHeaders(HttpHeaders&&) = default;
|
cannam@147
|
243 HttpHeaders& operator=(HttpHeaders&&) = default;
|
cannam@147
|
244
|
cannam@147
|
245 void clear();
|
cannam@147
|
246 // Clears all contents, as if the object was freshly-allocated. However, calling this rather
|
cannam@147
|
247 // than actually re-allocating the object may avoid re-allocation of internal objects.
|
cannam@147
|
248
|
cannam@147
|
249 HttpHeaders clone() const;
|
cannam@147
|
250 // Creates a deep clone of the HttpHeaders. The returned object owns all strings it references.
|
cannam@147
|
251
|
cannam@147
|
252 HttpHeaders cloneShallow() const;
|
cannam@147
|
253 // Creates a shallow clone of the HttpHeaders. The returned object references the same strings
|
cannam@147
|
254 // as the original, owning none of them.
|
cannam@147
|
255
|
cannam@147
|
256 kj::Maybe<kj::StringPtr> get(HttpHeaderId id) const;
|
cannam@147
|
257 // Read a header.
|
cannam@147
|
258
|
cannam@147
|
259 template <typename Func>
|
cannam@147
|
260 void forEach(Func&& func) const;
|
cannam@147
|
261 // Calls `func(name, value)` for each header in the set -- including headers that aren't mapped
|
cannam@147
|
262 // to IDs in the header table. Both inputs are of type kj::StringPtr.
|
cannam@147
|
263
|
cannam@147
|
264 void set(HttpHeaderId id, kj::StringPtr value);
|
cannam@147
|
265 void set(HttpHeaderId id, kj::String&& value);
|
cannam@147
|
266 // Sets a header value, overwriting the existing value.
|
cannam@147
|
267 //
|
cannam@147
|
268 // The String&& version is equivalent to calling the other version followed by takeOwnership().
|
cannam@147
|
269 //
|
cannam@147
|
270 // WARNING: It is the caller's responsibility to ensure that `value` remains valid until the
|
cannam@147
|
271 // HttpHeaders object is destroyed. This allows string literals to be passed without making a
|
cannam@147
|
272 // copy, but complicates the use of dynamic values. Hint: Consider using `takeOwnership()`.
|
cannam@147
|
273
|
cannam@147
|
274 void add(kj::StringPtr name, kj::StringPtr value);
|
cannam@147
|
275 void add(kj::StringPtr name, kj::String&& value);
|
cannam@147
|
276 void add(kj::String&& name, kj::String&& value);
|
cannam@147
|
277 // Append a header. `name` will be looked up in the header table, but if it's not mapped, the
|
cannam@147
|
278 // header will be added to the list of unmapped headers.
|
cannam@147
|
279 //
|
cannam@147
|
280 // The String&& versions are equivalent to calling the other version followed by takeOwnership().
|
cannam@147
|
281 //
|
cannam@147
|
282 // WARNING: It is the caller's responsibility to ensure that `name` and `value` remain valid
|
cannam@147
|
283 // until the HttpHeaders object is destroyed. This allows string literals to be passed without
|
cannam@147
|
284 // making a copy, but complicates the use of dynamic values. Hint: Consider using
|
cannam@147
|
285 // `takeOwnership()`.
|
cannam@147
|
286
|
cannam@147
|
287 void unset(HttpHeaderId id);
|
cannam@147
|
288 // Removes a header.
|
cannam@147
|
289 //
|
cannam@147
|
290 // It's not possible to remove a header by string name because non-indexed headers would take
|
cannam@147
|
291 // O(n) time to remove. Instead, construct a new HttpHeaders object and copy contents.
|
cannam@147
|
292
|
cannam@147
|
293 void takeOwnership(kj::String&& string);
|
cannam@147
|
294 void takeOwnership(kj::Array<char>&& chars);
|
cannam@147
|
295 void takeOwnership(HttpHeaders&& otherHeaders);
|
cannam@147
|
296 // Takes overship of a string so that it lives until the HttpHeaders object is destroyed. Useful
|
cannam@147
|
297 // when you've passed a dynamic value to set() or add() or parse*().
|
cannam@147
|
298
|
cannam@147
|
299 struct ConnectionHeaders {
|
cannam@147
|
300 // These headers govern details of the specific HTTP connection or framing of the content.
|
cannam@147
|
301 // Hence, they are managed internally within the HTTP library, and never appear in an
|
cannam@147
|
302 // HttpHeaders structure.
|
cannam@147
|
303
|
cannam@147
|
304 #define DECLARE_HEADER(id, name) \
|
cannam@147
|
305 kj::StringPtr id;
|
cannam@147
|
306 KJ_HTTP_FOR_EACH_CONNECTION_HEADER(DECLARE_HEADER)
|
cannam@147
|
307 #undef DECLARE_HEADER
|
cannam@147
|
308 };
|
cannam@147
|
309
|
cannam@147
|
310 struct Request {
|
cannam@147
|
311 HttpMethod method;
|
cannam@147
|
312 kj::StringPtr url;
|
cannam@147
|
313 ConnectionHeaders connectionHeaders;
|
cannam@147
|
314 };
|
cannam@147
|
315 struct Response {
|
cannam@147
|
316 uint statusCode;
|
cannam@147
|
317 kj::StringPtr statusText;
|
cannam@147
|
318 ConnectionHeaders connectionHeaders;
|
cannam@147
|
319 };
|
cannam@147
|
320
|
cannam@147
|
321 kj::Maybe<Request> tryParseRequest(kj::ArrayPtr<char> content);
|
cannam@147
|
322 kj::Maybe<Response> tryParseResponse(kj::ArrayPtr<char> content);
|
cannam@147
|
323 // Parse an HTTP header blob and add all the headers to this object.
|
cannam@147
|
324 //
|
cannam@147
|
325 // `content` should be all text from the start of the request to the first occurrance of two
|
cannam@147
|
326 // newlines in a row -- including the first of these two newlines, but excluding the second.
|
cannam@147
|
327 //
|
cannam@147
|
328 // The parse is performed with zero copies: The callee clobbers `content` with '\0' characters
|
cannam@147
|
329 // to split it into a bunch of shorter strings. The caller must keep `content` valid until the
|
cannam@147
|
330 // `HttpHeaders` is destroyed, or pass it to `takeOwnership()`.
|
cannam@147
|
331
|
cannam@147
|
332 kj::String serializeRequest(HttpMethod method, kj::StringPtr url,
|
cannam@147
|
333 const ConnectionHeaders& connectionHeaders) const;
|
cannam@147
|
334 kj::String serializeResponse(uint statusCode, kj::StringPtr statusText,
|
cannam@147
|
335 const ConnectionHeaders& connectionHeaders) const;
|
cannam@147
|
336 // Serialize the headers as a complete request or response blob. The blob uses '\r\n' newlines
|
cannam@147
|
337 // and includes the double-newline to indicate the end of the headers.
|
cannam@147
|
338
|
cannam@147
|
339 kj::String toString() const;
|
cannam@147
|
340
|
cannam@147
|
341 private:
|
cannam@147
|
342 HttpHeaderTable* table;
|
cannam@147
|
343
|
cannam@147
|
344 kj::Array<kj::StringPtr> indexedHeaders;
|
cannam@147
|
345 // Size is always table->idCount().
|
cannam@147
|
346
|
cannam@147
|
347 struct Header {
|
cannam@147
|
348 kj::StringPtr name;
|
cannam@147
|
349 kj::StringPtr value;
|
cannam@147
|
350 };
|
cannam@147
|
351 kj::Vector<Header> unindexedHeaders;
|
cannam@147
|
352
|
cannam@147
|
353 kj::Vector<kj::Array<char>> ownedStrings;
|
cannam@147
|
354
|
cannam@147
|
355 kj::Maybe<uint> addNoCheck(kj::StringPtr name, kj::StringPtr value);
|
cannam@147
|
356
|
cannam@147
|
357 kj::StringPtr cloneToOwn(kj::StringPtr str);
|
cannam@147
|
358
|
cannam@147
|
359 kj::String serialize(kj::ArrayPtr<const char> word1,
|
cannam@147
|
360 kj::ArrayPtr<const char> word2,
|
cannam@147
|
361 kj::ArrayPtr<const char> word3,
|
cannam@147
|
362 const ConnectionHeaders& connectionHeaders) const;
|
cannam@147
|
363
|
cannam@147
|
364 bool parseHeaders(char* ptr, char* end, ConnectionHeaders& connectionHeaders);
|
cannam@147
|
365
|
cannam@147
|
366 // TODO(perf): Arguably we should store a map, but header sets are never very long
|
cannam@147
|
367 // TODO(perf): We could optimize for common headers by storing them directly as fields. We could
|
cannam@147
|
368 // also add direct accessors for those headers.
|
cannam@147
|
369 };
|
cannam@147
|
370
|
cannam@147
|
371 class WebSocket {
|
cannam@147
|
372 public:
|
cannam@147
|
373 WebSocket(kj::Own<kj::AsyncIoStream> stream);
|
cannam@147
|
374 // Create a WebSocket wrapping the given I/O stream.
|
cannam@147
|
375
|
cannam@147
|
376 kj::Promise<void> send(kj::ArrayPtr<const byte> message);
|
cannam@147
|
377 kj::Promise<void> send(kj::ArrayPtr<const char> message);
|
cannam@147
|
378 };
|
cannam@147
|
379
|
cannam@147
|
380 class HttpClient {
|
cannam@147
|
381 // Interface to the client end of an HTTP connection.
|
cannam@147
|
382 //
|
cannam@147
|
383 // There are two kinds of clients:
|
cannam@147
|
384 // * Host clients are used when talking to a specific host. The `url` specified in a request
|
cannam@147
|
385 // is actually just a path. (A `Host` header is still required in all requests.)
|
cannam@147
|
386 // * Proxy clients are used when the target could be any arbitrary host on the internet.
|
cannam@147
|
387 // The `url` specified in a request is a full URL including protocol and hostname.
|
cannam@147
|
388
|
cannam@147
|
389 public:
|
cannam@147
|
390 struct Response {
|
cannam@147
|
391 uint statusCode;
|
cannam@147
|
392 kj::StringPtr statusText;
|
cannam@147
|
393 const HttpHeaders* headers;
|
cannam@147
|
394 kj::Own<kj::AsyncInputStream> body;
|
cannam@147
|
395 // `statusText` and `headers` remain valid until `body` is dropped.
|
cannam@147
|
396 };
|
cannam@147
|
397
|
cannam@147
|
398 struct Request {
|
cannam@147
|
399 kj::Own<kj::AsyncOutputStream> body;
|
cannam@147
|
400 // Write the request entity body to this stream, then drop it when done.
|
cannam@147
|
401 //
|
cannam@147
|
402 // May be null for GET and HEAD requests (which have no body) and requests that have
|
cannam@147
|
403 // Content-Length: 0.
|
cannam@147
|
404
|
cannam@147
|
405 kj::Promise<Response> response;
|
cannam@147
|
406 // Promise for the eventual respnose.
|
cannam@147
|
407 };
|
cannam@147
|
408
|
cannam@147
|
409 virtual Request request(HttpMethod method, kj::StringPtr url, const HttpHeaders& headers,
|
cannam@147
|
410 kj::Maybe<uint64_t> expectedBodySize = nullptr) = 0;
|
cannam@147
|
411 // Perform an HTTP request.
|
cannam@147
|
412 //
|
cannam@147
|
413 // `url` may be a full URL (with protocol and host) or it may be only the path part of the URL,
|
cannam@147
|
414 // depending on whether the client is a proxy client or a host client.
|
cannam@147
|
415 //
|
cannam@147
|
416 // `url` and `headers` need only remain valid until `request()` returns (they can be
|
cannam@147
|
417 // stack-allocated).
|
cannam@147
|
418 //
|
cannam@147
|
419 // `expectedBodySize`, if provided, must be exactly the number of bytes that will be written to
|
cannam@147
|
420 // the body. This will trigger use of the `Content-Length` connection header. Otherwise,
|
cannam@147
|
421 // `Transfer-Encoding: chunked` will be used.
|
cannam@147
|
422
|
cannam@147
|
423 struct WebSocketResponse {
|
cannam@147
|
424 uint statusCode;
|
cannam@147
|
425 kj::StringPtr statusText;
|
cannam@147
|
426 const HttpHeaders* headers;
|
cannam@147
|
427 kj::OneOf<kj::Own<kj::AsyncInputStream>, kj::Own<WebSocket>> upstreamOrBody;
|
cannam@147
|
428 // `statusText` and `headers` remain valid until `upstreamOrBody` is dropped.
|
cannam@147
|
429 };
|
cannam@147
|
430 virtual kj::Promise<WebSocketResponse> openWebSocket(
|
cannam@147
|
431 kj::StringPtr url, const HttpHeaders& headers, kj::Own<WebSocket> downstream);
|
cannam@147
|
432 // Tries to open a WebSocket. Default implementation calls send() and never returns a WebSocket.
|
cannam@147
|
433 //
|
cannam@147
|
434 // `url` and `headers` are invalidated when the returned promise resolves.
|
cannam@147
|
435
|
cannam@147
|
436 virtual kj::Promise<kj::Own<kj::AsyncIoStream>> connect(kj::String host);
|
cannam@147
|
437 // Handles CONNECT requests. Only relevant for proxy clients. Default implementation throws
|
cannam@147
|
438 // UNIMPLEMENTED.
|
cannam@147
|
439 };
|
cannam@147
|
440
|
cannam@147
|
441 class HttpService {
|
cannam@147
|
442 // Interface which HTTP services should implement.
|
cannam@147
|
443 //
|
cannam@147
|
444 // This interface is functionally equivalent to HttpClient, but is intended for applications to
|
cannam@147
|
445 // implement rather than call. The ergonomics and performance of the method signatures are
|
cannam@147
|
446 // optimized for the serving end.
|
cannam@147
|
447 //
|
cannam@147
|
448 // As with clients, there are two kinds of services:
|
cannam@147
|
449 // * Host services are used when talking to a specific host. The `url` specified in a request
|
cannam@147
|
450 // is actually just a path. (A `Host` header is still required in all requests, and the service
|
cannam@147
|
451 // may in fact serve multiple origins via this header.)
|
cannam@147
|
452 // * Proxy services are used when the target could be any arbitrary host on the internet, i.e. to
|
cannam@147
|
453 // implement an HTTP proxy. The `url` specified in a request is a full URL including protocol
|
cannam@147
|
454 // and hostname.
|
cannam@147
|
455
|
cannam@147
|
456 public:
|
cannam@147
|
457 class Response {
|
cannam@147
|
458 public:
|
cannam@147
|
459 virtual kj::Own<kj::AsyncOutputStream> send(
|
cannam@147
|
460 uint statusCode, kj::StringPtr statusText, const HttpHeaders& headers,
|
cannam@147
|
461 kj::Maybe<uint64_t> expectedBodySize = nullptr) = 0;
|
cannam@147
|
462 // Begin the response.
|
cannam@147
|
463 //
|
cannam@147
|
464 // `statusText` and `headers` need only remain valid until send() returns (they can be
|
cannam@147
|
465 // stack-allocated).
|
cannam@147
|
466 };
|
cannam@147
|
467
|
cannam@147
|
468 virtual kj::Promise<void> request(
|
cannam@147
|
469 HttpMethod method, kj::StringPtr url, const HttpHeaders& headers,
|
cannam@147
|
470 kj::AsyncInputStream& requestBody, Response& response) = 0;
|
cannam@147
|
471 // Perform an HTTP request.
|
cannam@147
|
472 //
|
cannam@147
|
473 // `url` may be a full URL (with protocol and host) or it may be only the path part of the URL,
|
cannam@147
|
474 // depending on whether the service is a proxy service or a host service.
|
cannam@147
|
475 //
|
cannam@147
|
476 // `url` and `headers` are invalidated on the first read from `requestBody` or when the returned
|
cannam@147
|
477 // promise resolves, whichever comes first.
|
cannam@147
|
478
|
cannam@147
|
479 class WebSocketResponse: public Response {
|
cannam@147
|
480 public:
|
cannam@147
|
481 kj::Own<WebSocket> startWebSocket(
|
cannam@147
|
482 uint statusCode, kj::StringPtr statusText, const HttpHeaders& headers,
|
cannam@147
|
483 WebSocket& upstream);
|
cannam@147
|
484 // Begin the response.
|
cannam@147
|
485 //
|
cannam@147
|
486 // `statusText` and `headers` need only remain valid until startWebSocket() returns (they can
|
cannam@147
|
487 // be stack-allocated).
|
cannam@147
|
488 };
|
cannam@147
|
489
|
cannam@147
|
490 virtual kj::Promise<void> openWebSocket(
|
cannam@147
|
491 kj::StringPtr url, const HttpHeaders& headers, WebSocketResponse& response);
|
cannam@147
|
492 // Tries to open a WebSocket. Default implementation calls request() and never returns a
|
cannam@147
|
493 // WebSocket.
|
cannam@147
|
494 //
|
cannam@147
|
495 // `url` and `headers` are invalidated when the returned promise resolves.
|
cannam@147
|
496
|
cannam@147
|
497 virtual kj::Promise<kj::Own<kj::AsyncIoStream>> connect(kj::String host);
|
cannam@147
|
498 // Handles CONNECT requests. Only relevant for proxy services. Default implementation throws
|
cannam@147
|
499 // UNIMPLEMENTED.
|
cannam@147
|
500 };
|
cannam@147
|
501
|
cannam@147
|
502 kj::Own<HttpClient> newHttpClient(HttpHeaderTable& responseHeaderTable, kj::Network& network,
|
cannam@147
|
503 kj::Maybe<kj::Network&> tlsNetwork = nullptr);
|
cannam@147
|
504 // Creates a proxy HttpClient that connects to hosts over the given network.
|
cannam@147
|
505 //
|
cannam@147
|
506 // `responseHeaderTable` is used when parsing HTTP responses. Requests can use any header table.
|
cannam@147
|
507 //
|
cannam@147
|
508 // `tlsNetwork` is required to support HTTPS destination URLs. Otherwise, only HTTP URLs can be
|
cannam@147
|
509 // fetched.
|
cannam@147
|
510
|
cannam@147
|
511 kj::Own<HttpClient> newHttpClient(HttpHeaderTable& responseHeaderTable, kj::AsyncIoStream& stream);
|
cannam@147
|
512 // Creates an HttpClient that speaks over the given pre-established connection. The client may
|
cannam@147
|
513 // be used as a proxy client or a host client depending on whether the peer is operating as
|
cannam@147
|
514 // a proxy.
|
cannam@147
|
515 //
|
cannam@147
|
516 // Note that since this client has only one stream to work with, it will try to pipeline all
|
cannam@147
|
517 // requests on this stream. If one request or response has an I/O failure, all subsequent requests
|
cannam@147
|
518 // fail as well. If the destination server chooses to close the connection after a response,
|
cannam@147
|
519 // subsequent requests will fail. If a response takes a long time, it blocks subsequent responses.
|
cannam@147
|
520 // If a WebSocket is opened successfully, all subsequent requests fail.
|
cannam@147
|
521
|
cannam@147
|
522 kj::Own<HttpClient> newHttpClient(HttpService& service);
|
cannam@147
|
523 kj::Own<HttpService> newHttpService(HttpClient& client);
|
cannam@147
|
524 // Adapts an HttpClient to an HttpService and vice versa.
|
cannam@147
|
525
|
cannam@147
|
526 struct HttpServerSettings {
|
cannam@147
|
527 kj::Duration headerTimeout = 15 * kj::SECONDS;
|
cannam@147
|
528 // After initial connection open, or after receiving the first byte of a pipelined request,
|
cannam@147
|
529 // the client must send the complete request within this time.
|
cannam@147
|
530
|
cannam@147
|
531 kj::Duration pipelineTimeout = 5 * kj::SECONDS;
|
cannam@147
|
532 // After one request/response completes, we'll wait up to this long for a pipelined request to
|
cannam@147
|
533 // arrive.
|
cannam@147
|
534 };
|
cannam@147
|
535
|
cannam@147
|
536 class HttpServer: private kj::TaskSet::ErrorHandler {
|
cannam@147
|
537 // Class which listens for requests on ports or connections and sends them to an HttpService.
|
cannam@147
|
538
|
cannam@147
|
539 public:
|
cannam@147
|
540 typedef HttpServerSettings Settings;
|
cannam@147
|
541
|
cannam@147
|
542 HttpServer(kj::Timer& timer, HttpHeaderTable& requestHeaderTable, HttpService& service,
|
cannam@147
|
543 Settings settings = Settings());
|
cannam@147
|
544 // Set up an HttpServer that directs incoming connections to the given service. The service
|
cannam@147
|
545 // may be a host service or a proxy service depending on whether you are intending to implement
|
cannam@147
|
546 // an HTTP server or an HTTP proxy.
|
cannam@147
|
547
|
cannam@147
|
548 kj::Promise<void> drain();
|
cannam@147
|
549 // Stop accepting new connections or new requests on existing connections. Finish any requests
|
cannam@147
|
550 // that are already executing, then close the connections. Returns once no more requests are
|
cannam@147
|
551 // in-flight.
|
cannam@147
|
552
|
cannam@147
|
553 kj::Promise<void> listenHttp(kj::ConnectionReceiver& port);
|
cannam@147
|
554 // Accepts HTTP connections on the given port and directs them to the handler.
|
cannam@147
|
555 //
|
cannam@147
|
556 // The returned promise never completes normally. It may throw if port.accept() throws. Dropping
|
cannam@147
|
557 // the returned promise will cause the server to stop listening on the port, but already-open
|
cannam@147
|
558 // connections will continue to be served. Destroy the whole HttpServer to cancel all I/O.
|
cannam@147
|
559
|
cannam@147
|
560 kj::Promise<void> listenHttp(kj::Own<kj::AsyncIoStream> connection);
|
cannam@147
|
561 // Reads HTTP requests from the given connection and directs them to the handler. A successful
|
cannam@147
|
562 // completion of the promise indicates that all requests received on the connection resulted in
|
cannam@147
|
563 // a complete response, and the client closed the connection gracefully or drain() was called.
|
cannam@147
|
564 // The promise throws if an unparseable request is received or if some I/O error occurs. Dropping
|
cannam@147
|
565 // the returned promise will cancel all I/O on the connection and cancel any in-flight requests.
|
cannam@147
|
566
|
cannam@147
|
567 private:
|
cannam@147
|
568 class Connection;
|
cannam@147
|
569
|
cannam@147
|
570 kj::Timer& timer;
|
cannam@147
|
571 HttpHeaderTable& requestHeaderTable;
|
cannam@147
|
572 HttpService& service;
|
cannam@147
|
573 Settings settings;
|
cannam@147
|
574
|
cannam@147
|
575 bool draining = false;
|
cannam@147
|
576 kj::ForkedPromise<void> onDrain;
|
cannam@147
|
577 kj::Own<kj::PromiseFulfiller<void>> drainFulfiller;
|
cannam@147
|
578
|
cannam@147
|
579 uint connectionCount = 0;
|
cannam@147
|
580 kj::Maybe<kj::Own<kj::PromiseFulfiller<void>>> zeroConnectionsFulfiller;
|
cannam@147
|
581
|
cannam@147
|
582 kj::TaskSet tasks;
|
cannam@147
|
583
|
cannam@147
|
584 HttpServer(kj::Timer& timer, HttpHeaderTable& requestHeaderTable, HttpService& service,
|
cannam@147
|
585 Settings settings, kj::PromiseFulfillerPair<void> paf);
|
cannam@147
|
586
|
cannam@147
|
587 kj::Promise<void> listenLoop(kj::ConnectionReceiver& port);
|
cannam@147
|
588
|
cannam@147
|
589 void taskFailed(kj::Exception&& exception) override;
|
cannam@147
|
590 };
|
cannam@147
|
591
|
cannam@147
|
592 // =======================================================================================
|
cannam@147
|
593 // inline implementation
|
cannam@147
|
594
|
cannam@147
|
595 inline void HttpHeaderId::requireFrom(HttpHeaderTable& table) const {
|
cannam@147
|
596 KJ_IREQUIRE(this->table == nullptr || this->table == &table,
|
cannam@147
|
597 "the provided HttpHeaderId is from the wrong HttpHeaderTable");
|
cannam@147
|
598 }
|
cannam@147
|
599
|
cannam@147
|
600 inline kj::Own<HttpHeaderTable> HttpHeaderTable::Builder::build() { return kj::mv(table); }
|
cannam@147
|
601 inline HttpHeaderTable& HttpHeaderTable::Builder::getFutureTable() { return *table; }
|
cannam@147
|
602
|
cannam@147
|
603 inline uint HttpHeaderTable::idCount() { return namesById.size(); }
|
cannam@147
|
604
|
cannam@147
|
605 inline kj::StringPtr HttpHeaderTable::idToString(HttpHeaderId id) {
|
cannam@147
|
606 id.requireFrom(*this);
|
cannam@147
|
607 return namesById[id.id];
|
cannam@147
|
608 }
|
cannam@147
|
609
|
cannam@147
|
610 inline kj::Maybe<kj::StringPtr> HttpHeaders::get(HttpHeaderId id) const {
|
cannam@147
|
611 id.requireFrom(*table);
|
cannam@147
|
612 auto result = indexedHeaders[id.id];
|
cannam@147
|
613 return result == nullptr ? kj::Maybe<kj::StringPtr>(nullptr) : result;
|
cannam@147
|
614 }
|
cannam@147
|
615
|
cannam@147
|
616 inline void HttpHeaders::unset(HttpHeaderId id) {
|
cannam@147
|
617 id.requireFrom(*table);
|
cannam@147
|
618 indexedHeaders[id.id] = nullptr;
|
cannam@147
|
619 }
|
cannam@147
|
620
|
cannam@147
|
621 template <typename Func>
|
cannam@147
|
622 inline void HttpHeaders::forEach(Func&& func) const {
|
cannam@147
|
623 for (auto i: kj::indices(indexedHeaders)) {
|
cannam@147
|
624 if (indexedHeaders[i] != nullptr) {
|
cannam@147
|
625 func(table->idToString(HttpHeaderId(table, i)), indexedHeaders[i]);
|
cannam@147
|
626 }
|
cannam@147
|
627 }
|
cannam@147
|
628
|
cannam@147
|
629 for (auto& header: unindexedHeaders) {
|
cannam@147
|
630 func(header.name, header.value);
|
cannam@147
|
631 }
|
cannam@147
|
632 }
|
cannam@147
|
633
|
cannam@147
|
634 } // namespace kj
|
cannam@147
|
635
|
cannam@147
|
636 #endif // KJ_COMPAT_HTTP_H_
|