annotate osx/include/kj/compat/http.h @ 83:ae30d91d2ffe

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