server.d 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971
  1. module vibe.http.internal.http2.server;
  2. import vibe.http.internal.http2.error;
  3. import vibe.http.internal.http2.multiplexing;
  4. import vibe.http.internal.http2.frame;
  5. import vibe.http.internal.http2.settings;
  6. import vibe.http.internal.http2.exchange;
  7. import vibe.http.internal.http2.hpack.tables;
  8. import vibe.http.internal.http2.hpack.hpack;
  9. import vibe.http.internal.http2.hpack.exception;
  10. import vibe.http.server;
  11. import vibe.container.internal.utilallocator;
  12. import vibe.core.log;
  13. import vibe.core.net;
  14. import vibe.core.core;
  15. import vibe.core.stream;
  16. import vibe.stream.tls;
  17. import vibe.internal.array;
  18. import vibe.internal.freelistref;
  19. import vibe.internal.interfaceproxy;
  20. import std.range;
  21. import std.base64;
  22. import std.traits;
  23. import std.bitmanip; // read from ubyte (decoding)
  24. import std.typecons;
  25. import std.conv : to;
  26. import std.exception : enforce;
  27. import std.algorithm : canFind; // alpn callback
  28. import std.algorithm.iteration;
  29. import std.variant : Algebraic;
  30. /*
  31. 3.2. Starting HTTP/2 for "http" URIs
  32. A client that makes a request for an "http" URI without prior
  33. knowledge about support for HTTP/2 on the next hop uses the HTTP
  34. Upgrade mechanism (Section 6.7 of [RFC7230]). The client does so by
  35. making an HTTP/1.1 request that includes an Upgrade header field with
  36. the "h2c" token. Such an HTTP/1.1 request MUST include exactly one
  37. HTTP2-Settings (Section 3.2.1) header field.
  38. For example:
  39. GET / HTTP/1.1
  40. Host: server.example.com
  41. Connection: Upgrade, HTTP2-Settings
  42. Upgrade: h2c
  43. HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>
  44. Requests that contain a payload body MUST be sent in their entirety
  45. before the client can send HTTP/2 frames. This means that a large
  46. request can block the use of the connection until it is completely
  47. sent.
  48. If concurrency of an initial request with subsequent requests is
  49. important, an OPTIONS request can be used to perform the upgrade to
  50. HTTP/2, at the cost of an additional round trip.
  51. A server that does not support HTTP/2 can respond to the request as
  52. though the Upgrade header field were absent:
  53. HTTP/1.1 200 OK
  54. Content-Length: 243
  55. Content-Type: text/html
  56. ...
  57. A server MUST ignore an "h2" token in an Upgrade header field.
  58. Presence of a token with "h2" implies HTTP/2 over TLS, which is
  59. instead negotiated as described in Section 3.3.
  60. A server that supports HTTP/2 accepts the upgrade with a 101
  61. (Switching Protocols) response. After the empty line that terminates
  62. the 101 response, the server can begin sending HTTP/2 frames. These
  63. frames MUST include a response to the request that initiated the
  64. upgrade.
  65. For example:
  66. HTTP/1.1 101 Switching Protocols
  67. Connection: Upgrade
  68. Upgrade: h2c
  69. [ HTTP/2 connection ...
  70. The first HTTP/2 frame sent by the server MUST be a server connection
  71. preface (Section 3.5) consisting of a SETTINGS frame (Section 6.5).
  72. Upon receiving the 101 response, the client MUST send a connection
  73. preface (Section 3.5), which includes a SETTINGS frame.
  74. The HTTP/1.1 request that is sent prior to upgrade is assigned a
  75. stream identifier of 1 (see Section 5.1.1) with default priority
  76. values (Section 5.3.5). Stream 1 is implicitly "half-closed" from
  77. the client toward the server (see Section 5.1), since the request is
  78. completed as an HTTP/1.1 request. After commencing the HTTP/2
  79. connection, stream 1 is used for the response.
  80. 3.2.1. HTTP2-Settings Header Field
  81. A request that upgrades from HTTP/1.1 to HTTP/2 MUST include exactly
  82. one "HTTP2-Settings" header field. The HTTP2-Settings header field
  83. is a connection-specific header field that includes parameters that
  84. govern the HTTP/2 connection, provided in anticipation of the server
  85. accepting the request to upgrade.
  86. HTTP2-Settings = token68
  87. A server MUST NOT upgrade the connection to HTTP/2 if this header
  88. field is not present or if more than one is present. A server MUST
  89. NOT send this header field.
  90. The content of the HTTP2-Settings header field is the payload of a
  91. SETTINGS frame (Section 6.5), encoded as a base64url string (that is,
  92. the URL- and filename-safe Base64 encoding described in Section 5 of
  93. [RFC4648], with any trailing '=' characters omitted). The ABNF
  94. [RFC5234] production for "token68" is defined in Section 2.1 of
  95. [RFC7235].
  96. Since the upgrade is only intended to apply to the immediate
  97. connection, a client sending the HTTP2-Settings header field MUST
  98. also send "HTTP2-Settings" as a connection option in the Connection
  99. header field to prevent it from being forwarded (see Section 6.1 of
  100. [RFC7230]).
  101. A server decodes and interprets these values as it would any other
  102. SETTINGS frame. Explicit acknowledgement of these settings
  103. (Section 6.5.3) is not necessary, since a 101 response serves as
  104. implicit acknowledgement. Providing these values in the upgrade
  105. request gives a client an opportunity to provide parameters prior to
  106. receiving any frames from the server.
  107. */
  108. /**
  109. * an ALPN callback which can be used to detect the "h2" protocol
  110. * must be set before initializing the server with 'listenHTTP'
  111. * if the protocol is not set, it replies with HTTP/1.1
  112. */
  113. TLSALPNCallback http2Callback = (string[] choices) {
  114. if (choices.canFind("h2")) return "h2";
  115. else return "http/1.1";
  116. };
  117. private alias TLSStreamType = ReturnType!(createTLSStreamFL!(InterfaceProxy!Stream));
  118. /* ==================================================== */
  119. /* CONNECTION INITIALIZATION */
  120. /* ==================================================== */
  121. /** h2c protocol switching ONLY: Check if SETTINGS payload is valid by trying to decode it
  122. * if !valid, close connection and refuse to upgrade (RFC)
  123. * if valid, send SWITCHING_PROTOCOL response and start an HTTP/2 connection handler
  124. */
  125. bool startHTTP2Connection(ConnectionStream, H)(ConnectionStream connection, string h2settings,
  126. HTTP2ServerContext context, HTTPServerResponse switchRes, H headers, string st,
  127. IAllocator alloc, ubyte[] resBody) @safe
  128. if (isConnectionStream!ConnectionStream)
  129. {
  130. // init settings
  131. HTTP2Settings settings;
  132. logTrace("Starting HTTP/2 connection");
  133. // try decoding settings
  134. if (settings.decode!Base64URL(h2settings)) {
  135. context.settings = settings;
  136. // initialize IndexingTable (HPACK)
  137. () @trusted {
  138. if(!context.hasTable) context.table = FreeListRef!IndexingTable(context.settings.headerTableSize);
  139. // save response converted to HTTP/2
  140. context.resFrame = alloc.makeArray!ubyte(buildHeaderFrame!(StartLine.RESPONSE)
  141. (st, headers, context, alloc));
  142. context.resFrame ~= resBody;
  143. } ();
  144. // send response
  145. switchRes.switchToHTTP2(&handleHTTP2Connection!ConnectionStream, context);
  146. return true;
  147. } else {
  148. // reply with a 400 (bad request) header
  149. switchRes.sendBadRequest();
  150. connection.close;
  151. return false;
  152. }
  153. }
  154. /** client AND server should send a connection preface
  155. * server should receive a connection preface from the client + SETTINGS Frame
  156. * server connection preface consists of a SETTINGS Frame
  157. */
  158. void handleHTTP2Connection(ConnectionStream)(ConnectionStream stream,
  159. TCPConnection connection, HTTP2ServerContext context, bool priorKnowledge=false) @safe
  160. if (isConnectionStream!ConnectionStream || is(ConnectionStream : TLSStreamType))
  161. {
  162. logTrace("HTTP/2 Connection Handler");
  163. // read the connection preface
  164. if(!priorKnowledge) {
  165. ubyte[24] h2connPreface;
  166. stream.read(h2connPreface);
  167. if(h2connPreface != "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") {
  168. logDebug("Ignoring invalid HTTP/2 client connection preface");
  169. return;
  170. }
  171. logTrace("Received client http2 connection preface");
  172. }
  173. // initialize Frame handler
  174. handleHTTP2FrameChain(stream, connection, context);
  175. }
  176. /* ==================================================== */
  177. /* FRAME HANDLING */
  178. /* ==================================================== */
  179. /// async frame handler: in charge of closing the connection if no data flows
  180. private void handleHTTP2FrameChain(ConnectionStream)(ConnectionStream stream, TCPConnection
  181. connection, HTTP2ServerContext context) @safe nothrow
  182. if (isConnectionStream!ConnectionStream || is(ConnectionStream : TLSStream))
  183. {
  184. logTrace("HTTP/2 Frame Chain Handler");
  185. static struct CB {
  186. ConnectionStream stream;
  187. TCPConnection connection;
  188. HTTP2ServerContext context;
  189. void opCall(bool st)
  190. {
  191. if (!st) connection.close;
  192. else runTask(&handleHTTP2FrameChain, stream, connection, context);
  193. }
  194. }
  195. while(true) {
  196. try {
  197. CB cb = {stream, connection, context};
  198. auto st = connection.waitForDataAsync(cb);
  199. final switch(st) {
  200. case WaitForDataAsyncStatus.waiting:
  201. logTrace("need to wait for more data asynchronously");
  202. return;
  203. case WaitForDataAsyncStatus.noMoreData:
  204. logTrace("connection closed by remote side");
  205. stream.finalize();
  206. connection.close();
  207. return;
  208. case WaitForDataAsyncStatus.dataAvailable:
  209. // start the frame handler
  210. bool close = handleHTTP2Frame(stream, connection, context);
  211. // determine if this connection needs to be closed
  212. if(close) {
  213. logTrace("Closing connection.");
  214. stream.finalize();
  215. connection.close();
  216. return;
  217. }
  218. }
  219. } catch (Exception e) {
  220. logException(e, "Failed to handle HTTP/2 frame chain");
  221. connection.close();
  222. return;
  223. }
  224. }
  225. }
  226. /// initializes an allocator and handles stream closing
  227. private bool handleHTTP2Frame(ConnectionStream)(ConnectionStream stream, TCPConnection
  228. connection, HTTP2ServerContext context) @trusted
  229. if (isConnectionStream!ConnectionStream || is(ConnectionStream : TLSStream))
  230. {
  231. import vibe.container.internal.utilallocator: RegionListAllocator;
  232. logTrace("HTTP/2 Frame Handler");
  233. bool close = false;
  234. () @trusted {
  235. version (VibeManualMemoryManagement)
  236. scope alloc = new RegionListAllocator!(shared(Mallocator), false)
  237. (1024, Mallocator.instance);
  238. else
  239. scope alloc = new RegionListAllocator!(shared(GCAllocator), true)
  240. (1024, GCAllocator.instance);
  241. if(!context.hasMultiplexer) context.multiplexer = FreeListRef!HTTP2Multiplexer(
  242. alloc,
  243. context.settings.maxConcurrentStreams,
  244. context.settings.initialWindowSize,
  245. context.settings.headerTableSize);
  246. if(!context.hasTable) context.table = FreeListRef!IndexingTable(context.settings.headerTableSize);
  247. // create a HTTP/2 Stream
  248. auto h2stream = HTTP2ConnectionStream!ConnectionStream(stream, 0, alloc);
  249. close = handleFrameAlloc(h2stream, connection, context, alloc);
  250. // if stream has to be closed
  251. if(h2stream.state == HTTP2StreamState.CLOSED) {
  252. try {
  253. closeStream(context.multiplexer, h2stream.streamId);
  254. } catch(Exception e) {
  255. logWarn("Unable to close stream: " ~ e.msg);
  256. close = true;
  257. }
  258. }
  259. } ();
  260. return close;
  261. }
  262. /// used (mixin) to check the validity of the received Frame w.r.to its stream ID
  263. private const string checkvalid = "enforceHTTP2(valid, \"Invalid stream ID\", HTTP2Error.STREAM_CLOSED);";
  264. /** Receives an HTTP2ConnectionStream, and handles the data received by decoding frames
  265. * Currently supports simple requests / responses
  266. * Stream Lifecycle is treated according to RFC 7540, Section 5.1
  267. */
  268. private bool handleFrameAlloc(ConnectionStream)(ref ConnectionStream stream, TCPConnection connection,
  269. HTTP2ServerContext context, IAllocator alloc) @trusted
  270. {
  271. logTrace("HTTP/2 Frame Handler (Alloc)");
  272. uint len = 0;
  273. // payload buffer
  274. auto rawBuf = AllocAppender!(ubyte[])(alloc);
  275. auto payload = AllocAppender!(ubyte[])(alloc);
  276. // Frame properties
  277. bool endStream = false;
  278. bool endHeaders = false;
  279. bool isAck = false;
  280. bool close = false;
  281. scope HTTP2FrameStreamDependency sdep;
  282. // frame struct
  283. scope HTTP2FrameHeader header;
  284. /* ==================================================== */
  285. /* read received header */
  286. /* ==================================================== */
  287. if(stream.canRead) {
  288. try {
  289. len = stream.readHeader(rawBuf);
  290. } catch (UncaughtException e) {
  291. // failed reading from stream, do not close the connection
  292. stream.state = HTTP2StreamState.CLOSED;
  293. return false;
  294. }
  295. } else {
  296. // failed reading from stream, do not close the connection
  297. //stream.state = HTTP2StreamState.CLOSED;
  298. return false;
  299. }
  300. // adjust buffer sizes
  301. rawBuf.reserve(len);
  302. payload.reserve(len);
  303. /* ==================================================== */
  304. /* read received payload */
  305. /* ==================================================== */
  306. if(len) stream.readPayload(rawBuf, len);
  307. /* ==================================================== */
  308. /* parse received Frame */
  309. /* ==================================================== */
  310. try {
  311. header = payload.unpackHTTP2Frame(rawBuf.data, endStream, endHeaders, isAck, sdep);
  312. } catch (HTTP2Exception e) {
  313. if (stream.state != HTTP2StreamState.IDLE || stream.streamId == 0) {
  314. ubyte[GOAWAYFrameLength] f;
  315. f.buildGOAWAYFrame(stream.streamId, e.code);
  316. stream.write(f);
  317. stream.state = HTTP2StreamState.CLOSED;
  318. logWarn("%s: %s", "Sent GOAWAY Frame", e.message);
  319. return true;
  320. } else {
  321. logDebug("Ignoring unsupported extension header.");
  322. return false;
  323. }
  324. } catch (Exception e) {
  325. logWarn(e.msg);
  326. }
  327. /* ==================================================== */
  328. /* register stream on MUX and determine Frame type */
  329. /* ==================================================== */
  330. try {
  331. auto valid = registerStream(context.multiplexer, header.streamId);
  332. logDebug("Received: "~to!string(header.type)~" on streamID "~to!string(header.streamId));
  333. enforceHTTP2(header.streamId % 2 != 0 || header.streamId == 0, "Clients cannot register even streams", HTTP2Error.PROTOCOL_ERROR);
  334. if(stream.needsContinuation) enforceHTTP2(header.type == HTTP2FrameType.CONTINUATION,
  335. "Expected continuation frame", HTTP2Error.PROTOCOL_ERROR);
  336. stream.streamId = header.streamId;
  337. final switch(header.type) {
  338. /* ==================================================== */
  339. /* DATA Frame (TODO) */
  340. /* ==================================================== */
  341. case HTTP2FrameType.DATA:
  342. mixin(checkvalid);
  343. if(endStream) {
  344. if(stream.state == HTTP2StreamState.HALF_CLOSED_LOCAL) {
  345. stream.state = HTTP2StreamState.CLOSED;
  346. closeStream(context.multiplexer, stream.streamId);
  347. } else if(stream.state == HTTP2StreamState.OPEN) {
  348. stream.state = HTTP2StreamState.HALF_CLOSED_REMOTE;
  349. } else if(stream.state == HTTP2StreamState.IDLE) {
  350. enforceHTTP2(false, "Invalid state", HTTP2Error.PROTOCOL_ERROR);
  351. } else {
  352. enforceHTTP2(false, "Stream closed", HTTP2Error.STREAM_CLOSED);
  353. }
  354. }
  355. break;
  356. /* ==================================================== */
  357. /* HEADERS Frame */
  358. /* ==================================================== */
  359. case HTTP2FrameType.HEADERS:
  360. mixin(checkvalid);
  361. enforceHTTP2(stream.streamId > 0, "Invalid stream ID", HTTP2Error.PROTOCOL_ERROR);
  362. stream.state = HTTP2StreamState.OPEN;
  363. if(sdep.isSet) {
  364. // update stream dependency with data in `sdep`
  365. }
  366. // save the header block for processing
  367. stream.putHeaderBlock(payload.data);
  368. if(endStream) {
  369. stream.state = HTTP2StreamState.HALF_CLOSED_REMOTE;
  370. }
  371. // parse headers in payload
  372. if(endHeaders) {
  373. logDebug("Received full HEADERS block");
  374. handleHTTP2HeadersFrame(stream, connection, context, alloc);
  375. } else {
  376. // wait for the next CONTINUATION frame until end_headers flag is set
  377. // END_STREAM flag does not count in this case
  378. logDebug("Incomplete HEADERS block, waiting for CONTINUATION frame.");
  379. close = handleFrameAlloc(stream, connection, context, alloc);
  380. }
  381. break;
  382. /* ==================================================== */
  383. /* PRIORITY Frame (TODO) */
  384. /* ==================================================== */
  385. case HTTP2FrameType.PRIORITY:
  386. // do not check validity since PRIORITY frames can be received on CLOSED
  387. // streams
  388. enforceHTTP2(stream.streamId > 0, "Invalid stream ID", HTTP2Error.PROTOCOL_ERROR);
  389. // update stream dependency with data in `sdep`
  390. break;
  391. /* ==================================================== */
  392. /* RST_STREAM Frame */
  393. /* ==================================================== */
  394. case HTTP2FrameType.RST_STREAM:
  395. enforceHTTP2(stream.state != HTTP2StreamState.IDLE || !valid,
  396. "Invalid state", HTTP2Error.PROTOCOL_ERROR);
  397. // reset stream in `closed` state
  398. if(stream.state != HTTP2StreamState.CLOSED) {
  399. closeStream(context.multiplexer, stream.streamId);
  400. }
  401. logDebug("RST_STREAM: Stream %d closed, error: %s",
  402. stream.streamId, cast(HTTP2Error)fromBytes(payload.data,4));
  403. break;
  404. /* ==================================================== */
  405. /* SETTINGS Frame */
  406. /* ==================================================== */
  407. case HTTP2FrameType.SETTINGS:
  408. if(!isAck) {
  409. handleHTTP2SettingsFrame(stream, connection, payload.data, header, context);
  410. } else {
  411. enforceHTTP2(payload.data.length == 0,
  412. "Invalid SETTINGS ACK (payload not empty)", HTTP2Error.FRAME_SIZE_ERROR);
  413. logDebug("Received SETTINGS ACK");
  414. }
  415. break;
  416. /* ==================================================== */
  417. /* PUSH_PROMISE Frame */
  418. /* ==================================================== */
  419. case HTTP2FrameType.PUSH_PROMISE:
  420. enforceHTTP2(false,
  421. "Client should not send PUSH_PROMISE Frames.", HTTP2Error.PROTOCOL_ERROR);
  422. break;
  423. /* ==================================================== */
  424. /* PING Frame */
  425. /* ==================================================== */
  426. case HTTP2FrameType.PING:
  427. if(!isAck) {
  428. // acknowledge ping with PING ACK Frame
  429. FixedAppender!(ubyte[], 17) buf;
  430. buf.createHTTP2FrameHeader(len, header.type, 0x1, header.streamId);
  431. // write PING Frame header
  432. stream.write(buf.data);
  433. // write PING Frame payload (equal to the received one)
  434. stream.write(payload.data);
  435. logDebug("Sent PING ACK response");
  436. }
  437. break;
  438. /* ==================================================== */
  439. /* GOAWAY Frame */
  440. /* ==================================================== */
  441. case HTTP2FrameType.GOAWAY:
  442. logDebug("Received GOAWAY Frame. Closing connection");
  443. stream.state = HTTP2StreamState.CLOSED;
  444. closeStream(context.multiplexer, stream.streamId);
  445. close = true;
  446. break;
  447. /* ==================================================== */
  448. /* WINDOW_UPDATE Frame */
  449. /* ==================================================== */
  450. case HTTP2FrameType.WINDOW_UPDATE:
  451. // can be received on closed streams (in case of pending data)
  452. enforceHTTP2(stream.state != HTTP2StreamState.IDLE || !valid ||
  453. stream.streamId == 0, "Invalid state", HTTP2Error.PROTOCOL_ERROR);
  454. auto inc = fromBytes(payload.data, 4);
  455. uint maxinc = 1 << 31;
  456. enforceHTTP2(inc > 0, "Invalid WINDOW_UPDATE increment", HTTP2Error.PROTOCOL_ERROR);
  457. // connection-based control window must be updated
  458. auto cw = connectionWindow(context.multiplexer);
  459. enforceHTTP2(cw + inc < maxinc, "Reached maximum WINDOW size",
  460. HTTP2Error.FLOW_CONTROL_ERROR);
  461. updateConnectionWindow(context.multiplexer, cw + inc);
  462. // per-stream control window must be updated (together with cw)
  463. if(stream.streamId > 0) {
  464. auto scw = streamConnectionWindow(context.multiplexer, stream.streamId);
  465. enforceHTTP2(scw + inc < maxinc, "Reached maximum WINDOW size",
  466. HTTP2Error.FLOW_CONTROL_ERROR);
  467. updateStreamConnectionWindow(context.multiplexer, stream.streamId, scw + inc);
  468. }
  469. // notify waiting DATA tasks if needed
  470. if(checkCondition(context.multiplexer, stream.streamId)) {
  471. logDebug("Notifying stopped tasks");
  472. notifyCondition(context.multiplexer);
  473. yield();
  474. }
  475. break;
  476. /* ==================================================== */
  477. /* CONTINUATION Frame
  478. /* ==================================================== */
  479. case HTTP2FrameType.CONTINUATION:
  480. // must be received immediately after a HEADERS Frame or a
  481. // CONTINUATION Frame
  482. enforceHTTP2(stream.state != HTTP2StreamState.IDLE, "Invalid state",
  483. HTTP2Error.PROTOCOL_ERROR);
  484. // add the received block to buffer
  485. stream.putHeaderBlock(payload.data);
  486. // process header block fragment in payload
  487. if(endHeaders) {
  488. logDebug("Received full HEADERS block");
  489. handleHTTP2HeadersFrame(stream, connection, context, alloc);
  490. } else {
  491. logDebug("Incomplete HEADERS block, waiting for CONTINUATION frame.");
  492. handleFrameAlloc(stream, connection, context, alloc);
  493. }
  494. break;
  495. }
  496. /* ==================================================== */
  497. /* `h2c`: First Response
  498. /* ==================================================== */
  499. static if(!is(ConnectionStream : TLSStream)) {
  500. if (context.resFrame) {
  501. auto l = context.resFrame.takeExactly(3).fromBytes(3) + 9;
  502. if(l < context.settings.maxFrameSize)
  503. {
  504. auto isEndStream = (context.resFrame.length > l) ? 0x0 : 0x1;
  505. context.resFrame[4] += 0x4 + isEndStream;
  506. try {
  507. stream.write(context.resFrame[0..l]);
  508. } catch (Exception e) {
  509. logWarn("Unable to write HEADERS Frame to stream");
  510. }
  511. } else {
  512. // TODO CONTINUATION frames
  513. assert(false);
  514. }
  515. auto resBody = context.resFrame[l..$];
  516. alloc.dispose(context.resFrame);
  517. // send DATA (body) if present
  518. // since the first response is part of HTTP/2 initialization,
  519. // this task is NOT executed asynchronously (for now) TODO
  520. if(resBody.length > 0) {
  521. auto dataFrame = AllocAppender!(ubyte[])(alloc);
  522. // create DATA Frame with END_STREAM (0x1) flag
  523. if(resBody.length > uint.max) assert(false, "TODO");
  524. // create DATA frame header
  525. dataFrame.createHTTP2FrameHeader(cast(uint)resBody.length, HTTP2FrameType.DATA, 0x1, 1);
  526. // append the DATA body
  527. dataFrame.put(resBody);
  528. // try writing data
  529. try {
  530. stream.write(dataFrame.data);
  531. } catch(Exception e) {
  532. logWarn("Unable to write DATA Frame to stream.");
  533. }
  534. logTrace("Sent DATA frame on streamID %s", stream.streamId);
  535. }
  536. }
  537. }
  538. closeStream(context.multiplexer, stream.streamId);
  539. } catch(HTTP2Exception e) {
  540. ubyte[GOAWAYFrameLength] f;
  541. f.buildGOAWAYFrame(stream.streamId, e.code);
  542. stream.write(f);
  543. logWarn("%s: %s", "Sent GOAWAY Frame", e.message);
  544. stream.state = HTTP2StreamState.CLOSED;
  545. return true;
  546. } catch(HPACKException e) {
  547. ubyte[GOAWAYFrameLength] f;
  548. f.buildGOAWAYFrame(stream.streamId, HTTP2Error.COMPRESSION_ERROR);
  549. stream.write(f);
  550. stream.state = HTTP2StreamState.CLOSED;
  551. logWarn("%s: %s", "Sent GOAWAY Frame", e.message);
  552. return true;
  553. } catch(Exception e) {
  554. logWarn(e.msg);
  555. }
  556. return close;
  557. }
  558. /// process an HEADERS frame
  559. void handleHTTP2HeadersFrame(Stream)(ref Stream stream, TCPConnection connection,
  560. HTTP2ServerContext context, IAllocator alloc)
  561. {
  562. // AllocAppender cannot be used here (TODO discuss)
  563. auto hdec = appender!(HTTP2HeaderTableField[])();
  564. // decode headers
  565. decodeHPACK(cast(immutable(ubyte)[])stream.headerBlock, hdec, context.table, alloc, context.settings.headerTableSize);
  566. // insert data in table
  567. hdec.data.each!((h) { if(h.index) context.table.insert(h); });
  568. // write a response (HEADERS + DATA according to request method)
  569. handleHTTP2Request(stream, connection, context, hdec.data, context.table, alloc);
  570. // clean the header block buffer
  571. stream.resetHeaderBlock();
  572. }
  573. /// handle SETTINGS frame exchange
  574. void handleHTTP2SettingsFrame(Stream)(ref Stream stream, TCPConnection connection, ubyte[] data, HTTP2FrameHeader header, HTTP2ServerContext context) @safe
  575. {
  576. // parse settings payload
  577. context.settings.unpackSettings(data);
  578. // update the connection window and notify waiting workers
  579. if(stream.streamId == 0) updateConnectionWindow(context.multiplexer, context.settings.initialWindowSize);
  580. updateStreamConnectionWindow(context.multiplexer, stream.streamId, context.settings.initialWindowSize);
  581. // notify waiting threads if needed
  582. if(checkCondition(context.multiplexer, stream.streamId)) {
  583. logTrace("Notifying stopped tasks");
  584. notifyCondition(context.multiplexer);
  585. //yield();
  586. }
  587. // acknowledge settings with SETTINGS ACK Frame
  588. FixedAppender!(ubyte[], 9) ackReply;
  589. ackReply.createHTTP2FrameHeader(0, header.type, 0x1, header.streamId);
  590. // new connection: must send a SETTINGS Frame as preface
  591. if(isConnectionPreface(context.multiplexer)) sendHTTP2SettingsFrame(stream, context);
  592. // write SETTINGS ACK
  593. stream.write(ackReply.data);
  594. logDebug("Sent SETTINGS ACK");
  595. }
  596. /// send a SETTINGS Frame
  597. void sendHTTP2SettingsFrame(Stream)(ref Stream stream, HTTP2ServerContext context) @safe
  598. {
  599. FixedAppender!(ubyte[], HTTP2HeaderLength+36) settingDst;
  600. settingDst.createHTTP2FrameHeader(36, HTTP2FrameType.SETTINGS, 0x0, 0);
  601. settingDst.serializeSettings(context.settings);
  602. stream.write(settingDst.data);
  603. logDebug("Sent SETTINGS Frame");
  604. }
  605. enum HTTP2StreamState {
  606. IDLE,
  607. RESERVED_LOCAL,
  608. RESERVED_REMOTE,
  609. OPEN,
  610. HALF_CLOSED_LOCAL,
  611. HALF_CLOSED_REMOTE,
  612. CLOSED
  613. }
  614. /** Represent a HTTP/2 Stream
  615. * The underlying connection can be TCPConnection or TLSStream
  616. * TODO: stream dependency, proper handling of stream IDs
  617. * approach: mantain a union of IDs so that only correct streams are initialized
  618. */
  619. struct HTTP2ConnectionStream(CS)
  620. {
  621. static assert(isConnectionStream!CS || is(CS : TLSStream) || isOutputStream!Stream);
  622. private {
  623. enum Parse { HEADER, PAYLOAD };
  624. CS m_conn;
  625. uint m_streamId;
  626. Parse toParse = Parse.HEADER;
  627. HTTP2StreamState m_state;
  628. AllocAppender!(ubyte[]) m_headerBlock;
  629. // Stream dependency TODO
  630. HTTP2FrameStreamDependency m_dependency;
  631. }
  632. // embed underlying connection
  633. alias m_conn this;
  634. this(CS)(ref CS conn, uint sid, IAllocator alloc) @safe
  635. {
  636. m_conn = conn;
  637. m_streamId = sid;
  638. m_state = HTTP2StreamState.IDLE;
  639. m_headerBlock = AllocAppender!(ubyte[])(alloc);
  640. }
  641. this(CS)(ref CS conn, IAllocator alloc) @safe
  642. {
  643. this(conn, 0, alloc);
  644. }
  645. @property CS connection() @safe { return m_conn; }
  646. @property HTTP2StreamState state() @safe @nogc { return m_state; }
  647. @property bool canRead() @safe @nogc
  648. {
  649. return (m_state == HTTP2StreamState.OPEN || m_state == HTTP2StreamState.IDLE);
  650. }
  651. /// set state according to Stream lifecycle (RFC 7540 section 5.1)
  652. @property void state(HTTP2StreamState st) @safe
  653. {
  654. switch(st) {
  655. // allowed: IDLE -> OPEN
  656. // OPEN -> OPEN
  657. case HTTP2StreamState.OPEN:
  658. if(m_state == HTTP2StreamState.IDLE ||
  659. m_state == HTTP2StreamState.OPEN)
  660. m_state = st;
  661. else enforceHTTP2(false, "Invalid state", HTTP2Error.PROTOCOL_ERROR);
  662. break;
  663. // allowed: OPEN -> HCLOCAL
  664. // RESERVED_REMOTE -> HCLOCAL
  665. // HCLOCAL -> HCLOCAL
  666. case HTTP2StreamState.HALF_CLOSED_LOCAL:
  667. if(m_state == HTTP2StreamState.OPEN ||
  668. m_state == HTTP2StreamState.RESERVED_REMOTE ||
  669. m_state == HTTP2StreamState.HALF_CLOSED_LOCAL)
  670. m_state = st;
  671. else enforceHTTP2(false, "Invalid state", HTTP2Error.PROTOCOL_ERROR);
  672. break;
  673. // allowed: OPEN -> HCREMOTE
  674. // RESERVED_LOCAL -> HCREMOTE
  675. // HCREMOTE -> HCREMOTE
  676. case HTTP2StreamState.HALF_CLOSED_REMOTE:
  677. if(m_state == HTTP2StreamState.OPEN ||
  678. m_state == HTTP2StreamState.RESERVED_LOCAL ||
  679. m_state == HTTP2StreamState.HALF_CLOSED_REMOTE)
  680. m_state = st;
  681. else enforceHTTP2(false, "Invalid state", HTTP2Error.PROTOCOL_ERROR);
  682. break;
  683. // allowed: all transitions to CLOSED
  684. // (RST_STREAM, GOAWAY permit this)
  685. case HTTP2StreamState.CLOSED:
  686. m_state = st;
  687. break;
  688. // specific to PUSH_PROMISE Frames
  689. case HTTP2StreamState.RESERVED_LOCAL:
  690. case HTTP2StreamState.RESERVED_REMOTE:
  691. if(m_state == HTTP2StreamState.IDLE) m_state = st;
  692. else enforceHTTP2(false, "Invalid state", HTTP2Error.PROTOCOL_ERROR);
  693. break;
  694. default:
  695. enforceHTTP2(false, "Invalid state", HTTP2Error.PROTOCOL_ERROR);
  696. }
  697. logTrace("Stream: %d state: %s", m_streamId, st);
  698. }
  699. @property uint streamId() @safe @nogc { return m_streamId; }
  700. @property void streamId(uint sid) @safe @nogc { m_streamId = sid; }
  701. @property HTTP2FrameStreamDependency dependency() @safe @nogc { return m_dependency; }
  702. @property ubyte[] headerBlock() @safe
  703. {
  704. assert(!m_headerBlock.data.empty, "No data in header block buffer");
  705. return m_headerBlock.data;
  706. }
  707. @property bool needsContinuation() @safe
  708. {
  709. return !m_headerBlock.data.empty;
  710. }
  711. /// reads from stream a frame header
  712. uint readHeader(R)(ref R dst) @safe
  713. {
  714. assert(toParse == Parse.HEADER);
  715. ubyte[HTTP2HeaderLength] buf; // should always be 9
  716. m_conn.read(buf);
  717. dst.put(buf);
  718. // length of payload
  719. auto len = dst.data[0..3].fromBytes(3);
  720. if(len > 0) toParse = Parse.PAYLOAD;
  721. return len;
  722. }
  723. /// reads from stream a frame payload
  724. void readPayload(R)(ref R dst, int len) @safe
  725. {
  726. assert(toParse == Parse.PAYLOAD);
  727. toParse = Parse.HEADER;
  728. ubyte[8] buf = void;
  729. /// perform multiple reads until payload is over (@nogc compatibility)
  730. while(len > 0) {
  731. auto end = (len < buf.length) ? len : buf.length;
  732. len -= m_conn.read(buf[0..end], IOMode.all);
  733. dst.put(buf[0..end]);
  734. }
  735. }
  736. /// save a HEADERS / CONTINUATION block for processing
  737. void putHeaderBlock(T)(T src) @safe
  738. if(isInputRange!T && is(ElementType!T : ubyte))
  739. {
  740. m_headerBlock.put(src);
  741. }
  742. void resetHeaderBlock() @trusted
  743. {
  744. m_headerBlock.reset(AppenderResetMode.freeData);
  745. }
  746. void finalize() @safe { }
  747. }
  748. unittest {
  749. import vibe.core.core : runApplication;
  750. // empty handler, just to test if protocol switching works
  751. void handleReq(scope HTTPServerRequest req, scope HTTPServerResponse res)
  752. @safe {
  753. if (req.path == "/")
  754. res.writeBody("Hello, World! This response is sent through HTTP/2");
  755. }
  756. auto settings = new HTTPServerSettings();
  757. settings.port = 8090;
  758. settings.bindAddresses = ["localhost"];
  759. listenHTTP(settings, &handleReq);
  760. //runApplication();
  761. }
  762. unittest {
  763. import vibe.core.core : runApplication;
  764. void handleRequest(scope HTTPServerRequest req, scope HTTPServerResponse res)
  765. @safe {
  766. if (req.path == "/")
  767. res.writeBody("Hello, World! This response is sent through HTTP/2\n");
  768. }
  769. auto settings = new HTTPServerSettings;
  770. settings.port = 8091;
  771. settings.bindAddresses = ["127.0.0.1", "192.168.1.131"];
  772. settings.tlsContext = createTLSContext(TLSContextKind.server);
  773. settings.tlsContext.useCertificateChainFile("tests/server.crt");
  774. settings.tlsContext.usePrivateKeyFile("tests/server.key");
  775. // set alpn callback to support HTTP/2
  776. // should accept the 'h2' protocol request
  777. settings.tlsContext.alpnCallback(http2Callback);
  778. // dummy, just for testing
  779. listenHTTP(settings, &handleRequest);
  780. //runApplication();
  781. }