proxygen
SPDYCodec.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-present, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree. An additional grant
7  * of patent rights can be found in the PATENTS file in the same directory.
8  *
9  */
11 
12 #include <algorithm>
13 #include <boost/algorithm/string.hpp>
14 #include <folly/Conv.h>
15 #include <folly/Memory.h>
16 #include <folly/ScopeGuard.h>
17 #include <folly/String.h>
18 #include <folly/io/Cursor.h>
19 #include <glog/logging.h>
27 #include <vector>
28 
29 using folly::IOBuf;
30 using folly::IOBufQueue;
32 using folly::io::Cursor;
36 using std::pair;
37 using std::string;
38 using std::unique_ptr;
39 using std::vector;
40 
41 namespace proxygen {
42 
43 namespace {
44 
45 // Sizes, in bytes, of various types and parts of SPDY frames
46 const size_t kFrameSizeDataCommon = 8; // common prefix of all data frames
47 const size_t kFrameSizeControlCommon = 8; // common prefix of all ctrl frames
48 const size_t kFrameSizeSynStream = 10; // SYN_STREAM
49 const size_t kFrameSizeSynReplyv3 = 4; // SPDYv3's SYN_REPLY is shorter
50 const size_t kFrameSizeRstStream = 8; // RST_STREAM
51 const size_t kFrameSizeGoawayv3 = 8; // GOAWAY, SPDYv3
52 const size_t kFrameSizeHeaders = 4; // HEADERS
53 const size_t kFrameSizePing = 4; // PING
54 const size_t kFrameSizeSettings = 4; // SETTINGS
55 const size_t kFrameSizeSettingsEntry = 8; // Each entry in SETTINGS
56 const size_t kFrameSizeWindowUpdate = 8; // WINDOW_UPDATE
57  // name/value pair
58 const size_t kFrameSizeNameValuev3 = 4; // The size in bytes of a
59  // name/value pair
60 const size_t kPriShiftv3 = 5; // How many bits to shift pri, v3
61 
62 const size_t kMaxUncompressed = 96 * 1024; // 96kb ought be enough for anyone
63 
64 #define CTRL_MASK 0x80
65 #define FLAGS_MASK 0xff000000
66 #define STREAM_ID_MASK 0x7fffffff
67 #define VERSION_MASK 0x7fff
68 #define DELTA_WINDOW_SIZE_MASK 0x7fffffff
69 
70 /* The number of bytes of the frame header. */
71 #define FRAME_HEADER_LEN 8
72 
73 // SPDY flags
74 const uint8_t kFlagFin = 0x01;
75 
85 uint32_t flagsAndLength(uint8_t flags, uint32_t length) {
86  length &= 0x00ffffff;
87  length |= (int32_t(flags) << 24);
88  return length;
89 }
90 
91 void appendUint32(uint8_t*& dst, size_t value) {
92  *(uint32_t*)dst = htonl(uint32_t(value));
93  dst += 4;
94 }
95 
96 uint32_t parseUint32(Cursor* cursor) {
97  auto chunk = cursor->peek();
98  if (LIKELY(chunk.second >= sizeof(uint32_t))) {
99  cursor->skip(sizeof(uint32_t));
100  return ntohl(*(uint32_t*)chunk.first);
101  }
102  return cursor->readBE<uint32_t>();
103 }
104 
105 class SPDYSessionFailed : public std::exception {
106  public:
107  explicit SPDYSessionFailed(spdy::GoawayStatusCode inStatus)
108  : statusCode(inStatus) {}
109 
111 };
112 
113 class SPDYStreamFailed : public std::exception {
114  public:
115  SPDYStreamFailed(bool inIsNew, uint32_t inStreamID,
116  uint32_t inStatus,
117  const std::string& inMsg = empty_string)
118  : isNew(inIsNew),
119  streamID(inStreamID),
120  statusCode(inStatus) {
121  message = folly::to<std::string>("new=", isNew, " streamID=", streamID,
122  " statusCode=", statusCode, " message=",
123  inMsg);
124  }
125 
126  ~SPDYStreamFailed() throw() override {}
127 
128  const char* what() const throw() override { return message.c_str(); }
129 
130  bool isNew;
134 };
135 
136 } // anonynous namespace
137 
139  // XXX: We new and leak the static here intentionally so it doesn't get
140  // destroyed during a call to exit() when threads are still processing
141  // requests resulting in spurious shutdown crashes.
142 
143  // Indexed by SPDYVersion
144  static const auto spdyVersions = new std::vector<SPDYVersionSettings> {
145  // SPDY2 no longer supported; should it ever be added back the lines in which
146  // this codec creates compress/Header objects need to be updated as SPDY2
147  // constant header names are different from the set of common header names.
148  // SPDY3
151  spdy::kSessionProtoNameSPDY3, parseUint32, appendUint32,
152  (const unsigned char*)kSPDYv3Dictionary, sizeof(kSPDYv3Dictionary),
153  0x8003, kFrameSizeSynReplyv3, kFrameSizeNameValuev3,
154  kFrameSizeGoawayv3, kPriShiftv3, 3, 0, SPDYVersion::SPDY3,
156  // SPDY3.1
159  spdy::kSessionProtoNameSPDY3, parseUint32, appendUint32,
160  (const unsigned char*)kSPDYv3Dictionary, sizeof(kSPDYv3Dictionary),
161  0x8003, kFrameSizeSynReplyv3, kFrameSizeNameValuev3,
162  kFrameSizeGoawayv3, kPriShiftv3, 3, 1, SPDYVersion::SPDY3_1,
164  };
165  auto intVersion = static_cast<unsigned>(version);
166  CHECK_LT(intVersion, spdyVersions->size());
167  return (*spdyVersions)[intVersion];
168 }
169 
171  int spdyCompressionLevel /* = Z_NO_COMPRESSION */)
172  : HTTPParallelCodec(direction),
173  versionSettings_(getVersionSettings(version)),
174  frameState_(FrameState::FRAME_HEADER),
175  ctrl_(false),
176  headerCodec_(spdyCompressionLevel, versionSettings_) {
177  VLOG(4) << "creating SPDY/" << static_cast<int>(versionSettings_.majorVersion)
178  << "." << static_cast<int>(versionSettings_.minorVersion)
179  << " codec";
180 
181  // Limit uncompressed headers to 128kb
182  headerCodec_.setMaxUncompressed(kMaxUncompressed);
184 }
185 
187 }
188 
190  maxFrameLength_ = maxFrameLength;
191 }
192 
194  headerCodec_.setMaxUncompressed(maxUncompressed);
195 }
196 
198  switch (versionSettings_.version) {
201  };
202  LOG(FATAL) << "unreachable";
204 }
205 
207  return userAgent_;
208 }
209 
211  return versionSettings_.majorVersion > 2;
212 }
213 
215  return versionSettings_.majorVersion > 3 ||
217 }
218 
219 void SPDYCodec::checkLength(uint32_t expectedLength, const std::string& msg) {
220  if (length_ != expectedLength) {
221  LOG_IF(ERROR, length_ == 4 && msg != "GOAWAY")
222  << msg << ": invalid length " << length_ << " != " << expectedLength;
223  throw SPDYSessionFailed(spdy::GOAWAY_PROTOCOL_ERROR);
224  }
225 }
226 
227 void SPDYCodec::checkMinLength(uint32_t minLength, const std::string& msg) {
228  if (length_ < minLength) {
229  LOG(ERROR) << msg << ": invalid length " << length_ << " < " << minLength;
230  throw SPDYSessionFailed(spdy::GOAWAY_PROTOCOL_ERROR);
231  }
232 }
233 
234 size_t SPDYCodec::onIngress(const folly::IOBuf& buf) {
235  size_t bytesParsed = 0;
236  currentIngressBuf_ = &buf;
237  try {
238  bytesParsed = parseIngress(buf);
239  } catch (const SPDYSessionFailed& ex) {
240  failSession(ex.statusCode);
241  bytesParsed = buf.computeChainDataLength();
242  }
243  return bytesParsed;
244 }
245 
247  const size_t chainLength = buf.computeChainDataLength();
248  Cursor cursor(&buf);
249  size_t avail = cursor.totalLength();
250 
251  // This can parse beyond the current IOBuf
252  for (; avail > 0; avail = cursor.totalLength()) {
253  if (frameState_ == FrameState::FRAME_HEADER) {
254  if (avail < FRAME_HEADER_LEN) {
255  // Make the caller buffer until we get a full frame header
256  break;
257  }
258  auto data = cursor.peek();
259  ctrl_ = (data.first[0] & CTRL_MASK);
260  if (ctrl_) {
261  version_ = cursor.readBE<uint16_t>() & VERSION_MASK;
262  type_ = cursor.readBE<uint16_t>();
264  LOG(ERROR) << "Invalid version=" << version_;
265  throw SPDYSessionFailed(spdy::GOAWAY_PROTOCOL_ERROR);
266  }
267  } else {
268  streamId_ = cursor.readBE<uint32_t>() & STREAM_ID_MASK;
269  }
270  length_ = cursor.readBE<uint32_t>();
271  flags_ = (length_ & FLAGS_MASK) >> 24;
272  length_ &= ~FLAGS_MASK;
273  if (ctrl_) {
274  if (length_ > maxFrameLength_) {
276  type_ == spdy::HEADERS) {
277  uint32_t stream_id = cursor.readBE<uint32_t>() & STREAM_ID_MASK;
278  failStream(true, stream_id, spdy::RST_FRAME_TOO_LARGE);
279  // Compression/stream state is out of sync now
280  }
281  // Since maxFrameLength_ must be at least 8kb and most control frames
282  // have fixed size, only an invalid settings or credential frame can
283  // land here. For invalid credential frames we must send a goaway,
284  // and a settings frame would have > 1023 pairs, of which none are
285  // allowed to be duplicates. Just fail everything.
286  LOG(ERROR) << "excessive frame size length_=" << length_;
287  throw SPDYSessionFailed(spdy::GOAWAY_PROTOCOL_ERROR);
288  }
289  frameState_ = FrameState::CTRL_FRAME_DATA;
290  callback_->onFrameHeader(0, flags_, length_, type_, version_);
291  } else {
292  frameState_ = FrameState::DATA_FRAME_DATA;
293  callback_->onFrameHeader(streamId_, flags_, length_, type_);
294  }
295  } else if (frameState_ == FrameState::CTRL_FRAME_DATA) {
296  if (avail < length_) {
297  // Make the caller buffer the rest of the control frame.
298  // We could attempt to decompress incomplete name/value blocks,
299  // but for now we're favoring simplicity.
300  VLOG(6) << "Need more data: length_=" << length_ << " avail=" << avail;
301  break;
302  }
303  try {
304  onControlFrame(cursor);
305  } catch (const SPDYStreamFailed& ex) {
306  failStream(ex.isNew, ex.streamID, ex.statusCode, ex.what());
307  }
308  frameState_ = FrameState::FRAME_HEADER;
309  } else if (avail > 0 || length_ == 0) {
310  // Data frame data. Pass everything we have up to the frame boundary
311  DCHECK(FrameState::DATA_FRAME_DATA == frameState_);
312 
313  uint32_t toClone = (avail > std::numeric_limits<uint32_t>::max()) ?
314  std::numeric_limits<uint32_t>::max() : static_cast<uint32_t>(avail);
315  toClone = std::min(toClone, length_);
316  std::unique_ptr<IOBuf> chunk;
317  cursor.clone(chunk, toClone);
319  streamId_, std::move(chunk), 0);
320  length_ -= toClone;
321  }
322 
323  // Fin handling
324  if (length_ == 0) {
325  if (flags_ & spdy::CTRL_FLAG_FIN) {
327  "onMessageComplete", streamId_, false);
328  }
329  frameState_ = FrameState::FRAME_HEADER;
330  }
331  }
332  return chainLength - avail;
333 }
334 
336  switch (type_) {
337  case spdy::SYN_STREAM:
338  {
339  checkMinLength(kFrameSizeSynStream, "SYN_STREAM");
340  streamId_ = cursor.readBE<uint32_t>() & STREAM_ID_MASK;
341  uint32_t assocStream = cursor.readBE<uint32_t>();
342  uint8_t pri = cursor.read<uint8_t>() >> versionSettings_.priShift;
343  uint8_t slot = cursor.read<uint8_t>();
344  length_ -= kFrameSizeSynStream;
345  auto result = decodeHeaders(cursor);
346  checkLength(0, "SYN_STREAM");
347  onSynStream(assocStream, pri, slot,
348  result.headers, headerCodec_.getDecodedSize());
349  break;
350  }
351  case spdy::SYN_REPLY:
352  {
354  streamId_ = cursor.readBE<uint32_t>() & STREAM_ID_MASK;
356  if (version_ == 2) {
357  // 2 byte unused
358  cursor.skip(2);
359  }
360  auto result = decodeHeaders(cursor);
361  checkLength(0, "SYN_REPLY");
362  onSynReply(result.headers,
364  break;
365  }
366  case spdy::RST_STREAM:
367  {
368  checkLength(kFrameSizeRstStream, "RST");
369  streamId_ = cursor.readBE<uint32_t>() & STREAM_ID_MASK;
370  uint32_t statusCode = cursor.readBE<uint32_t>();
371  onRstStream(statusCode);
372  break;
373  }
374  case spdy::SETTINGS:
375  {
376  checkMinLength(kFrameSizeSettings, "SETTINGS");
377  uint32_t numSettings = cursor.readBE<uint32_t>();
378  length_ -= sizeof(uint32_t);
379  if (length_ / 8 < numSettings) {
380  LOG(ERROR) << "SETTINGS: number of settings to high. "
381  << length_ << " < 8 * " << numSettings;
382  throw SPDYSessionFailed(spdy::GOAWAY_PROTOCOL_ERROR);
383  }
385  for (uint32_t i = 0; i < numSettings; i++) {
386  uint32_t id = 0;
387  if (version_ == 2) {
388  id = cursor.readLE<uint32_t>();
389  } else {
390  id = cursor.readBE<uint32_t>();
391  }
392  uint32_t value = cursor.readBE<uint32_t>();
393  uint8_t flags = (id & FLAGS_MASK) >> 24;
394  id &= ~FLAGS_MASK;
395  settings.emplace_back(flags, id, value);
396  }
397  onSettings(settings);
398  break;
399  }
400  case spdy::NOOP:
401  VLOG(4) << "Noop received. Doing nothing.";
402  checkLength(0, "NOOP");
403  break;
404  case spdy::PING:
405  {
406  checkLength(kFrameSizePing, "PING");
407  uint32_t unique_id = cursor.readBE<uint32_t>();
408  onPing(unique_id);
409  break;
410  }
411  case spdy::GOAWAY:
412  {
414  uint32_t lastStream = cursor.readBE<uint32_t>();
415  uint32_t statusCode = 0;
416  if (version_ == 3) {
417  statusCode = cursor.readBE<uint32_t>();
418  }
419  onGoaway(lastStream, statusCode);
420  break;
421  }
422  case spdy::HEADERS:
423  {
424  // Note: this is for the HEADERS frame type, not the initial headers
425  checkMinLength(kFrameSizeHeaders, "HEADERS");
426  streamId_ = cursor.readBE<uint32_t>() & STREAM_ID_MASK;
427  length_ -= kFrameSizeHeaders;
428  if (version_ == 2) {
429  // 2 byte unused
430  cursor.skip(2);
431  length_ -= 2;
432  }
433  auto result = decodeHeaders(cursor);
434  checkLength(0, "HEADERS");
435  onHeaders(result.headers);
436  break;
437  }
438  case spdy::WINDOW_UPDATE:
439  {
440  checkLength(kFrameSizeWindowUpdate, "WINDOW_UPDATE");
441  streamId_ = cursor.readBE<uint32_t>() & STREAM_ID_MASK;
442  uint32_t delta = cursor.readBE<uint32_t>() & DELTA_WINDOW_SIZE_MASK;
443  onWindowUpdate(delta);
444  break;
445  }
446  case spdy::CREDENTIAL:
447  {
448  VLOG(4) << "Skipping unsupported/deprecated CREDENTIAL frame";
449  // Fall through to default case
450  }
451  default:
452  VLOG(3) << "unimplemented control frame type " << type_
453  << ", frame length: " << length_;
454  // From spdy spec:
455  // Control frame processing requirements:
456  // If an endpoint receives a control frame for a type it does not
457  // recognize, it must ignore the frame.
458 
459  // Consume rest of the frame to skip processing it further
460  cursor.skip(length_);
461  length_ = 0;
462  return;
463  }
464 }
465 
467  auto result = headerCodec_.decode(cursor, length_);
468  if (result.hasError()) {
469  auto err = result.error();
473  // Fail stream only for FRAME_TOO_LARGE error
476  }
477  throw SPDYSessionFailed(spdy::GOAWAY_PROTOCOL_ERROR);
478  }
479  // For other types of errors we throw a stream error
480  bool newStream = (type_ != spdy::HEADERS);
481  throw SPDYStreamFailed(newStream, streamId_, spdy::RST_PROTOCOL_ERROR,
482  "Error parsing header: " + folly::to<string>(err));
483  }
484 
485  length_ -= result->bytesConsumed;
486  return *result;
487 }
488 
490  return (versionSettings_.majorVersion == 2 &&
499 }
500 
501 // Add the SPDY-specific header fields that hold the
502 // equivalent of the HTTP/1.x request-line or status-line.
503 unique_ptr<IOBuf> SPDYCodec::encodeHeaders(
504  const HTTPMessage& msg, vector<Header>& allHeaders,
505  uint32_t headroom, HTTPHeaderSize* size) {
506 
507  // We explicitly provide both the code and header name here
508  // as HTTP_HEADER_OTHER does not map to kNameVersionv3 and we don't want a
509  // perf penalty hash kNameVersionv3 to HTTP_HEADER_OTHER
510  allHeaders.emplace_back(
512 
513  // Add the HTTP headers supplied by the caller, but skip
514  // any per-hop headers that aren't supported in SPDY.
515  msg.getHeaders().forEachWithCode([&] (HTTPHeaderCode code,
516  const string& name,
517  const string& value) {
518  static const std::bitset<256> s_perHopHeaderCodes{
519  [] {
520  std::bitset<256> bs;
521  // SPDY per-hop headers
522  bs[HTTP_HEADER_CONNECTION] = true;
523  bs[HTTP_HEADER_HOST] = true;
524  bs[HTTP_HEADER_KEEP_ALIVE] = true;
525  bs[HTTP_HEADER_PROXY_CONNECTION] = true;
527  bs[HTTP_HEADER_UPGRADE] = true;
528  return bs;
529  }()
530  };
531 
532  if (s_perHopHeaderCodes[code] || isSPDYReserved(name)) {
533  VLOG(3) << "Dropping SPDY reserved header " << name;
534  return;
535  }
536  if (name.length() == 0) {
537  VLOG(2) << "Dropping header with empty name";
538  return;
539  }
540  if (versionSettings_.majorVersion == 2 && value.length() == 0) {
541  VLOG(2) << "Dropping header \"" << name
542  << "\" with empty value for spdy/2";
543  return;
544  }
545  allHeaders.emplace_back(code, name, value);
546  });
547 
549  auto out = headerCodec_.encode(allHeaders);
550  if (size) {
551  *size = headerCodec_.getEncodedSize();
552  }
553 
554  return out;
555 }
556 
558  const HTTPMessage& msg, uint32_t headroom, HTTPHeaderSize* size) {
559 
560  // Note: the header-sorting code works with pointers to strings.
561  // The role of this local status string is to hold the generated
562  // status code long enough for the sort (done later within the
563  // same scope) to be able to access it.
564  string status;
565 
566  const HTTPHeaders& headers = msg.getHeaders();
567  vector<Header> allHeaders;
568  allHeaders.reserve(headers.size() + 4);
569 
570  if (msg.getStatusMessage().empty()) {
571  status = folly::to<string>(msg.getStatusCode());
572  } else {
573  status = folly::to<string>(msg.getStatusCode(), " ",
574  msg.getStatusMessage());
575  }
576  allHeaders.emplace_back(HTTP_HEADER_COLON_STATUS, status);
577  // See comment above regarding status
578  string date;
579  if (!headers.exists(HTTP_HEADER_DATE)) {
581  allHeaders.emplace_back(HTTP_HEADER_DATE, date);
582  }
583 
584  return encodeHeaders(msg, allHeaders, headroom, size);
585 }
586 
588  const HTTPMessage& msg,
589  bool isPushed,
590  uint32_t headroom,
591  HTTPHeaderSize* size) {
592 
593  const HTTPHeaders& headers = msg.getHeaders();
594  vector<Header> allHeaders;
595  allHeaders.reserve(headers.size() + 6);
596 
597  const string& method = msg.getMethodString();
598  static const string https("https");
599  static const string http("http");
600  const string& scheme = msg.isSecure() ? https : http;
601  string path = msg.getURL();
602 
603  CHECK_GT(versionSettings_.majorVersion, 2) << "SPDY/2 no longer supported";
604 
605  if (isPushed) {
606  const string& pushString = msg.getPushStatusStr();
607  allHeaders.emplace_back(HTTP_HEADER_COLON_STATUS, pushString);
608  } else {
609  allHeaders.emplace_back(HTTP_HEADER_COLON_METHOD, method);
610  }
611  allHeaders.emplace_back(HTTP_HEADER_COLON_SCHEME, scheme);
612  allHeaders.emplace_back(HTTP_HEADER_COLON_PATH, path);
613  if (versionSettings_.majorVersion == 3) {
614  DCHECK(headers.exists(HTTP_HEADER_HOST));
615  const string& host = headers.getSingleOrEmpty(HTTP_HEADER_HOST);
616  // We explicitly provide both the code and header name here
617  // as HTTP_HEADER_OTHER does not map to kNameHostv3 and we don't want a
618  // perf penalty hash kNameHostv3 to HTTP_HEADER_OTHER
619  allHeaders.emplace_back(HTTP_HEADER_OTHER, versionSettings_.hostStr, host);
620 }
621 
622  return encodeHeaders(msg, allHeaders, headroom, size);
623 }
624 
627  const HTTPMessage& msg,
628  bool eom,
629  HTTPHeaderSize* size) {
630  if (!isStreamIngressEgressAllowed(stream)) {
631  VLOG(2) << "Suppressing SYN_STREAM/REPLY for stream=" << stream <<
632  " ingressGoawayAck_=" << ingressGoawayAck_;
633  if (size) {
634  size->compressed = 0;
635  size->uncompressed = 0;
636  }
637  return;
638  }
640  generateSynStream(stream, 0, writeBuf, msg, eom, size);
641  } else {
642  generateSynReply(stream, writeBuf, msg, eom, size);
643  }
644 }
645 
648  const HTTPMessage& msg,
649  StreamID assocStream,
650  bool eom,
651  HTTPHeaderSize* size) {
652  DCHECK(assocStream != NoStream);
653  if (!isStreamIngressEgressAllowed(stream)) {
654  VLOG(2) << "Suppressing SYN_STREAM/REPLY for stream=" << stream <<
655  " ingressGoawayAck_=" << ingressGoawayAck_;
656  if (size) {
657  size->compressed = 0;
658  size->uncompressed = 0;
659  }
660  return;
661  }
662  generateSynStream(stream, assocStream, writeBuf, msg, eom, size);
663 }
664 
666  StreamID assocStream,
668  const HTTPMessage& msg,
669  bool eom,
670  HTTPHeaderSize* size) {
671  // Pushed streams must have an even streamId and an odd assocStream
672  CHECK((assocStream == NoStream && (stream % 2 == 1)) ||
673  ((stream % 2 == 0) && (assocStream % 2 == 1))) <<
674  "Invalid stream ids stream=" << stream << " assocStream=" << assocStream;
675 
676  // Serialize the compressed representation of the headers
677  // first because we need to write its length. The
678  // serializeRequestHeaders() method allocates an IOBuf to
679  // hold the headers, but we can tell it to reserve
680  // enough headroom at the start of the IOBuf to hold
681  // the metadata we'll need to add once we know the
682  // length.
683  uint32_t fieldsSize = kFrameSizeSynStream;
684  uint32_t headroom = kFrameSizeControlCommon + fieldsSize;
685  bool isPushed = (assocStream != NoStream);
686  unique_ptr<IOBuf> out(serializeRequestHeaders(msg, isPushed,
687  headroom, size));
688 
689  // The length field in the SYN_STREAM header holds the number
690  // of bytes that follow it. That's the length of the fields
691  // specific to the SYN_STREAM message (all of which come after
692  // the length field) plus the length of the serialized header
693  // name/value block.
694  uint32_t len = fieldsSize + out->computeChainDataLength();
695 
696  // Generate a control frame header of type SYN_STREAM within
697  // the headroom that serializeRequestHeaders() reserved for us
698  // at the start of the IOBuf.
700  if (assocStream != NoStream) {
702  }
703  if (eom) {
704  flags |= spdy::CTRL_FLAG_FIN;
705  }
706  out->prepend(headroom);
707  RWPrivateCursor cursor(out.get());
708  cursor.writeBE(versionSettings_.controlVersion);
709  cursor.writeBE(uint16_t(spdy::SYN_STREAM));
710  cursor.writeBE(flagsAndLength(flags, len));
711  cursor.writeBE(uint32_t(stream));
712  cursor.writeBE(uint32_t(assocStream));
713  // If the message set HTTP/2 priority instead of SPDY priority, we lose
714  // priority information since we can't collapse it.
715  // halve priority for SPDY/2
716  uint8_t pri = msg.getPriority() >> (3 - versionSettings_.majorVersion);
717  cursor.writeBE(uint16_t(pri << (versionSettings_.priShift + 8)));
718 
719  // Now that we have a complete SYN_STREAM control frame, append
720  // it to the writeBuf.
721  writeBuf.append(std::move(out));
722 }
723 
726  const HTTPMessage& msg,
727  bool eom,
728  HTTPHeaderSize* size) {
729  // Serialize the compressed representation of the headers
730  // first because we need to write its length. The
731  // serializeResponseHeaders() method allocates an IOBuf to
732  // hold the headers, but we can tell it to reserve
733  // enough headroom at the start of the IOBuf to hold
734  // the metadata we'll need to add once we know the
735  // length.
736  uint32_t headroom = kFrameSizeControlCommon + versionSettings_.synReplySize;
737  unique_ptr<IOBuf> out(serializeResponseHeaders(msg, headroom, size));
738 
739  // The length field in the SYN_REPLY header holds the number
740  // of bytes that follow it. That's the length of the fields
741  // specific to the SYN_REPLY message (all of which come after
742  // the length field) plus the length of the serialized header
743  // name/value block.
745 
746  // Generate a control frame header of type SYN_REPLY within
747  // the headroom that we serializeResponseHeaders() reserved for us
748  // at the start of the IOBuf.1
750  out->prepend(headroom);
751  RWPrivateCursor cursor(out.get());
752  cursor.writeBE(versionSettings_.controlVersion);
753  cursor.writeBE(uint16_t(spdy::SYN_REPLY));
754  cursor.writeBE(flagsAndLength(flags, len));
755  cursor.writeBE(uint32_t(stream)); // TODO: stream should never be bigger than 2^31
756  if (versionSettings_.majorVersion == 2) {
757  cursor.writeBE(uint16_t(0));
758  }
759 
760  // Now that we have a complete SYN_REPLY control frame, append
761  // it to the writeBuf.
762  writeBuf.append(std::move(out));
763 }
764 
767  std::unique_ptr<folly::IOBuf> chain,
768  folly::Optional<uint8_t> /*padding*/,
769  bool eom) {
770  if (!isStreamIngressEgressAllowed(stream)) {
771  VLOG(2) << "Suppressing DATA for stream=" << stream <<
772  " ingressGoawayAck_=" << ingressGoawayAck_;
773  return 0;
774  }
775  size_t len = chain->computeChainDataLength();
776  if (len == 0) {
777  return len;
778  }
779 
780  // TODO if the data length is 2^24 or greater, split it into
781  // multiple data frames. Proxygen should never be writing that
782  // much data at once, but other apps that use this codec might.
783  CHECK_LT(len, (1 << 24));
784 
785  uint8_t flags = (eom) ? kFlagFin : 0;
786  generateDataFrame(writeBuf, uint32_t(stream), flags, len, std::move(chain));
787  return len;
788 }
789 
791  StreamID /*stream*/,
792  size_t /*length*/) {
793  // SPDY chunk headers are built into the data frames
794  return 0;
795 }
796 
798  StreamID /*stream*/) {
799  // SPDY has no chunk terminator
800  return 0;
801 }
802 
804  StreamID /*stream*/,
805  const HTTPHeaders& /*trailers*/) {
806  // TODO generate a HEADERS frame? An additional HEADERS frame
807  // somewhere after the SYN_REPLY seems to be the SPDY equivalent
808  // of HTTP/1.1's trailers.
809  return 0;
810 }
811 
813  StreamID stream) {
814  VLOG(4) << "sending EOM for stream=" << stream;
815  if (!isStreamIngressEgressAllowed(stream)) {
816  VLOG(2) << "Suppressing EOM for stream=" << stream <<
817  " ingressGoawayAck_=" << ingressGoawayAck_;
818  return 0;
819  }
820  generateDataFrame(writeBuf, uint32_t(stream), kFlagFin, 0, nullptr);
821  return 8; // size of data frame header
822 }
823 
826  ErrorCode code) {
827  DCHECK_GT(stream, 0);
828  VLOG(4) << "sending RST_STREAM for stream=" << stream
829  << " with code=" << getErrorCodeString(code);
830 
831  // Suppress any EOM callback for the current frame.
832  if (stream == streamId_) {
833  flags_ &= ~spdy::CTRL_FLAG_FIN;
834  }
835 
836  if (!isStreamIngressEgressAllowed(stream)) {
837  VLOG(2) << "Suppressing RST_STREAM for stream=" << stream <<
838  " ingressGoawayAck_=" << ingressGoawayAck_;
839  return 0;
840  }
841 
843  const size_t frameSize = kFrameSizeControlCommon + kFrameSizeRstStream;
844  const size_t expectedLength = writeBuf.chainLength() + frameSize;
845  QueueAppender appender(&writeBuf, frameSize);
847  appender.writeBE(uint16_t(spdy::RST_STREAM));
848  appender.writeBE(flagsAndLength(0, kFrameSizeRstStream));
849  appender.writeBE(uint32_t(stream));
850  appender.writeBE(rstStatusSupported(statusCode) ?
851  statusCode : (uint32_t)spdy::RST_PROTOCOL_ERROR);
852  DCHECK_EQ(writeBuf.chainLength(), expectedLength);
853  return frameSize;
854 }
855 
857  StreamID lastStream,
858  ErrorCode code,
859  std::unique_ptr<folly::IOBuf> debugData) {
861  const size_t frameSize = kFrameSizeControlCommon +
863 
864  DCHECK_LE(lastStream, egressGoawayAck_) << "Cannot increase last good stream";
865  egressGoawayAck_ = lastStream;
866  if (sessionClosing_ == ClosingState::CLOSING) {
867  VLOG(4) << "Not sending GOAWAY for closed session";
868  return 0;
869  }
870  const size_t expectedLength = writeBuf.chainLength() + frameSize;
871  QueueAppender appender(&writeBuf, frameSize);
873 
874  if (code != ErrorCode::NO_ERROR) {
875  sessionClosing_ = ClosingState::CLOSING;
876  }
877 
878  string debugInfo = (debugData) ?
879  folly::to<string>(" with debug info=",
880  (char*)debugData->data()) : "";
881  VLOG(4) << "Sending GOAWAY with last acknowledged stream="
882  << lastStream << " with code=" << getErrorCodeString(code)
883  << debugInfo;
884 
885  appender.writeBE(uint16_t(spdy::GOAWAY));
886  appender.writeBE(flagsAndLength(0, versionSettings_.goawaySize));
887  appender.writeBE(uint32_t(lastStream));
888  if (versionSettings_.majorVersion == 3) {
889  appender.writeBE(statusCode);
890  }
891  switch (sessionClosing_) {
892  case ClosingState::OPEN:
893  sessionClosing_ = ClosingState::CLOSING;
894  break;
895  case ClosingState::OPEN_WITH_GRACEFUL_DRAIN_ENABLED:
896  if (lastStream == std::numeric_limits<int32_t>::max()) {
897  sessionClosing_ = ClosingState::FIRST_GOAWAY_SENT;
898  } else {
899  // The user of this codec decided not to do the double goaway
900  // drain
901  sessionClosing_ = ClosingState::CLOSING;
902  }
903  break;
904  case ClosingState::FIRST_GOAWAY_SENT:
905  sessionClosing_ = ClosingState::CLOSING;
906  break;
907  case ClosingState::CLOSING:
908  break;
909  case ClosingState::CLOSED:
910  LOG(FATAL) << "unreachable";
911  break;
912  }
913  DCHECK_EQ(writeBuf.chainLength(), expectedLength);
914  return frameSize;
915 }
916 
918  const auto id = nextEgressPingID_;
919  nextEgressPingID_ += 2;
920  VLOG(4) << "Generating ping request with id=" << id;
921  return generatePingCommon(writeBuf, id);
922 }
923 
925  VLOG(4) << "Generating ping reply with id=" << uniqueID;
926  return generatePingCommon(writeBuf, uniqueID);
927 }
928 
930  const size_t frameSize = kFrameSizeControlCommon + kFrameSizePing;
931  const size_t expectedLength = writeBuf.chainLength() + frameSize;
932  QueueAppender appender(&writeBuf, frameSize);
934  appender.writeBE(uint16_t(spdy::PING));
935  appender.writeBE(flagsAndLength(0, kFrameSizePing));
936  appender.writeBE(uint32_t(uniqueID));
937  DCHECK_EQ(writeBuf.chainLength(), expectedLength);
938  return frameSize;
939 }
940 
942  auto numSettings = egressSettings_.getNumSettings();
943  for (const auto& setting: egressSettings_.getAllSettings()) {
944  if (!spdy::httpToSpdySettingsId(setting.id)) {
945  numSettings--;
946  }
947  }
948  VLOG(4) << "generating " << (unsigned)numSettings << " settings";
949  const size_t frameSize = kFrameSizeControlCommon + kFrameSizeSettings +
950  (kFrameSizeSettingsEntry * numSettings);
951  const size_t expectedLength = writeBuf.chainLength() + frameSize;
952  QueueAppender appender(&writeBuf, frameSize);
954  appender.writeBE(uint16_t(spdy::SETTINGS));
955  appender.writeBE(flagsAndLength(spdy::FLAG_SETTINGS_CLEAR_SETTINGS,
956  kFrameSizeSettings +
957  kFrameSizeSettingsEntry * numSettings));
958  appender.writeBE(uint32_t(numSettings));
959  for (const auto& setting: egressSettings_.getAllSettings()) {
960  auto settingId = spdy::httpToSpdySettingsId(setting.id);
961  if (!settingId) {
962  LOG(WARNING) << "Invalid SpdySetting " << (uint32_t)setting.id;
963  continue;
964  }
965  VLOG(5) << " writing setting with id=" << *settingId
966  << ", value=" << setting.value;
967  if (versionSettings_.majorVersion == 2) {
968  // ID: 24-bits in little-endian byte order.
969  // This is inconsistent with other values in SPDY and
970  // is the result of a bug in the initial v2 implementation.
971  appender.writeLE(flagsAndLength(0, *settingId));
972  } else {
973  appender.writeBE(flagsAndLength(0, *settingId));
974  }
975  appender.writeBE<uint32_t>(setting.value);
976  }
977  DCHECK_EQ(writeBuf.chainLength(), expectedLength);
978  return frameSize;
979 }
980 
983  uint32_t delta) {
984  if (versionSettings_.majorVersion < 3 ||
985  (stream == NoStream && versionSettings_.majorVersion == 3 &&
987  return 0;
988  }
989 
990  if (!isStreamIngressEgressAllowed(stream)) {
991  VLOG(2) << "Suppressing WINDOW_UPDATE for stream=" << stream <<
992  " ingressGoawayAck_=" << ingressGoawayAck_;
993  return 0;
994  }
995 
996  VLOG(4) << "generating window update for stream=" << stream
997  << ": Processed " << delta << " bytes";
998  const size_t frameSize = kFrameSizeControlCommon + kFrameSizeWindowUpdate;
999  const size_t expectedLength = writeBuf.chainLength() + frameSize;
1000  QueueAppender appender(&writeBuf, frameSize);
1003  appender.writeBE(flagsAndLength(0, kFrameSizeWindowUpdate));
1004  appender.writeBE(uint32_t(stream)); // TODO: ensure stream < 2^31
1005  appender.writeBE(delta); // TODO: delta should never be bigger than 2^31
1006  DCHECK_EQ(writeBuf.chainLength(), expectedLength);
1007  return frameSize;
1008 }
1009 
1011  PriorityQueue& queue,
1013  uint8_t) {
1015  // For SPDY, we always create 8 virtual nodes regardless of maxLevel
1016  for (uint8_t pri = 0; pri < 8; pri++) {
1017  queue.addPriorityNode(HTTPCodec::MAX_STREAM_ID + pri, parent);
1018  parent = HTTPCodec::MAX_STREAM_ID + pri;
1019  }
1020  return 0;
1021 }
1022 
1025 }
1026 
1029 }
1030 
1033  uint32_t length,
1034  unique_ptr<IOBuf> payload) {
1035  const size_t frameSize = kFrameSizeDataCommon;
1036  uint64_t payloadLength = 0;
1037  if (payload && !payload->isSharedOne() &&
1038  payload->headroom() >= frameSize &&
1039  writeBuf.tailroom() < frameSize) {
1040  // Use the headroom in payload for the frame header.
1041  // Make it appear that the payload IOBuf is empty and retreat so
1042  // appender can access the headroom
1043  payloadLength = payload->length();
1044  payload->trimEnd(payloadLength);
1045  payload->retreat(frameSize);
1046  auto tail = payload->pop();
1047  writeBuf.append(std::move(payload));
1048  payload = std::move(tail);
1049  }
1050  QueueAppender cursor(&writeBuf, frameSize);
1051  cursor.writeBE(uint32_t(streamID));
1052  cursor.writeBE(flagsAndLength(flags, length));
1053  writeBuf.postallocate(payloadLength);
1054  writeBuf.append(std::move(payload));
1055  return kFrameSizeDataCommon + length;
1056 }
1057 
1058 unique_ptr<HTTPMessage>
1060  StreamID assocStreamID,
1061  const HeaderPieceList& inHeaders) {
1062  unique_ptr<HTTPMessage> msg(new HTTPMessage());
1063  HTTPHeaders& headers = msg->getHeaders();
1064  bool newStream = (type_ != spdy::HEADERS);
1065 
1066  bool hasScheme = false;
1067  bool hasPath = false;
1068  bool hasContentLength = false;
1069 
1070  // Number of fields must be even
1071  CHECK_EQ((inHeaders.size() & 1), 0);
1072  for (unsigned i = 0; i < inHeaders.size(); i += 2) {
1073  uint8_t off = 0;
1074  uint32_t len = inHeaders[i].str.size();
1075  if (len > 1 && inHeaders[i].str[0] == ':') {
1076  off = 1; // also signals control header
1077  len--;
1078  }
1079  folly::StringPiece name(inHeaders[i].str, off, len);
1080  folly::StringPiece value = inHeaders[i + 1].str;
1081  VLOG(5) << "Header " << name << ": " << value;
1082  bool nameOk = CodecUtil::validateHeaderName(name);
1083  bool valueOk = false;
1084  bool isPath = false;
1085  bool isMethod = false;
1086  if (nameOk) {
1087  if (name == "content-length") {
1088  if (hasContentLength) {
1089  throw SPDYStreamFailed(false, streamID, 400,
1090  "Multiple content-length headers");
1091  }
1092  hasContentLength = true;
1093  }
1094  if ((version_ == 2 && name == "url") ||
1095  (version_ == 3 && off && name == "path")) {
1096  valueOk = CodecUtil::validateURL(value);
1097  isPath = true;
1098  if (hasPath) {
1099  throw SPDYStreamFailed(false, streamID, 400,
1100  "Multiple paths in header");
1101  }
1102  hasPath = true;
1103  } else if ((version_ == 2 || off) && name == "method") {
1104  valueOk = CodecUtil::validateMethod(value);
1105  isMethod = true;
1106  if (value == "CONNECT") {
1107  // We don't support CONNECT request for SPDY
1108  valueOk = false;
1109  }
1110  } else {
1112  }
1113  }
1114  if (!nameOk || !valueOk) {
1115  if (newStream) {
1116  deliverOnMessageBegin(streamID, assocStreamID, nullptr);
1117  }
1118  partialMsg_ = std::move(msg);
1119  throw SPDYStreamFailed(false, streamID, 400, "Bad header value");
1120  }
1121  bool add = false;
1122  if (off || version_ == 2) {
1123  if (isMethod) {
1124  msg->setMethod(value);
1125  } else if (isPath) {
1126  msg->setURL(value.str());
1127  } else if (name == "version") {
1128  if (caseInsensitiveEqual(value, "http/1.0")) {
1129  msg->setHTTPVersion(1, 0);
1130  } else {
1131  msg->setHTTPVersion(1, 1);
1132  }
1133  } else if (version_ == 3 && name == "host") {
1134  headers.add(HTTP_HEADER_HOST, value.str());
1135  } else if (name == "scheme") {
1136  hasScheme = true;
1137  if (value == "https") {
1138  msg->setSecure(true);
1139  }
1140  } else if (name == "status") {
1141  if (direction == TransportDirection::UPSTREAM && !assocStreamID) {
1142  folly::StringPiece codePiece;
1143  folly::StringPiece reasonPiece;
1144  if (value.contains(' ')) {
1145  folly::split<false>(' ', value, codePiece, reasonPiece);
1146  } else {
1147  codePiece = value;
1148  }
1149  int32_t code = -1;
1150  try {
1151  code = folly::to<unsigned int>(codePiece);
1152  } catch (const std::range_error& ex) {
1153  // Toss out the range error cause the exception will get it
1154  }
1155  if (code >= 100 && code <= 999) {
1156  msg->setStatusCode(code);
1157  msg->setStatusMessage(reasonPiece.str());
1158  } else {
1159  msg->setStatusCode(0);
1160  headers.add(name, value);
1161  partialMsg_ = std::move(msg);
1162  throw SPDYStreamFailed(newStream, streamID,
1164  "Invalid status code");
1165  }
1166  } else if (!assocStreamID) {
1167  if (version_ == 2) {
1168  headers.add("Status", value);
1169  }
1170  } else { // is a push status since there is an assocStreamID?
1171  // If there exists a push status, save it.
1172  // If there does not, for now, we *eat* the push status.
1173  if (value.size() > 0) {
1174  int16_t code = -1;
1175  try {
1176  code = folly::to<uint16_t>(value);
1177  } catch (const std::range_error& ex) {
1178  // eat the push status
1179  }
1180  if (code >= 100 && code <= 999) {
1181  msg->setPushStatusCode(code);
1182  } else {
1183  // eat the push status.
1184  }
1185  }
1186  }
1187  } else if (version_ == 2) {
1188  add = true;
1189  }
1190  } else {
1191  add = true;
1192  }
1193  if (add) {
1194  if (!inHeaders[i].isMultiValued() && headers.exists(name)) {
1195  headers.add(name, value);
1196  partialMsg_ = std::move(msg);
1197  throw SPDYStreamFailed(newStream, streamID, spdy::RST_PROTOCOL_ERROR,
1198  "Duplicate header value");
1199  }
1200  headers.add(name, value);
1201  }
1202  }
1203  if (assocStreamID &&
1204  (!headers.exists(HTTP_HEADER_HOST) || !hasScheme || !hasPath)) {
1205  // Fail a server push without host, scheme or path headers
1206  throw SPDYStreamFailed(newStream, streamID, 400, "Bad Request");
1207  }
1208  if (direction == TransportDirection::DOWNSTREAM) {
1209  if (version_ == 2 && !headers.exists(HTTP_HEADER_HOST)) {
1210  ParseURL url(msg->getURL());
1211  if (url.valid()) {
1212  headers.add(HTTP_HEADER_HOST, url.hostAndPort());
1213  }
1214  }
1215 
1216  const string& accept_encoding =
1218  if (accept_encoding.empty()) {
1219  headers.add(HTTP_HEADER_ACCEPT_ENCODING, "gzip, deflate");
1220  } else {
1221  bool hasGzip = false;
1222  bool hasDeflate = false;
1223  if (!CodecUtil::hasGzipAndDeflate(accept_encoding, hasGzip, hasDeflate)) {
1224  string new_encoding = accept_encoding;
1225  if (!hasGzip) {
1226  new_encoding.append(", gzip");
1227  }
1228  if (!hasDeflate) {
1229  new_encoding.append(", deflate");
1230  }
1231  headers.set(HTTP_HEADER_ACCEPT_ENCODING, new_encoding);
1232  }
1233  }
1234  }
1235  return msg;
1236 }
1237 
1239  StreamID assocStreamID,
1240  const HeaderPieceList& headers,
1241  int8_t pri,
1242  const HTTPHeaderSize& size) {
1244  LOG(ERROR) << "Invalid version=" << version_;
1245  throw SPDYSessionFailed(spdy::GOAWAY_PROTOCOL_ERROR);
1246  }
1247 
1248  unique_ptr<HTTPMessage> msg = parseHeaders(transportDirection_,
1249  streamID, assocStreamID, headers);
1250  msg->setIngressHeaderSize(size);
1251 
1252  msg->setAdvancedProtocolString(versionSettings_.protocolVersionString);
1253  // Normalize priority to 3 bits in HTTPMessage.
1254  pri <<= (3 - versionSettings_.majorVersion);
1255  msg->setPriority(pri);
1256  msg->setHTTP2Priority(std::make_tuple(HTTPCodec::MAX_STREAM_ID + pri,
1257  false, 255));
1258  deliverOnMessageBegin(streamID, assocStreamID, msg.get());
1259 
1260  if ((flags_ & spdy::CTRL_FLAG_FIN) == 0) {
1261  // If it there are DATA frames coming, consider it chunked
1262  msg->setIsChunked(true);
1263  }
1264  if (userAgent_.empty()) {
1265  userAgent_ = msg->getHeaders().getSingleOrEmpty(HTTP_HEADER_USER_AGENT);
1266  }
1268  "onHeadersComplete", streamID, std::move(msg));
1269 }
1270 
1272  HTTPMessage* msg) {
1273  if (assocStreamID) {
1275  "onPushMessageBegin", streamID, assocStreamID,
1276  msg);
1277  } else {
1279  "onMessageBegin", streamID, msg);
1280  }
1281 }
1282 
1284  uint8_t pri,
1285  uint8_t /*slot*/,
1286  const HeaderPieceList& headers,
1287  const HTTPHeaderSize& size) {
1288  VLOG(4) << "Got SYN_STREAM, stream=" << streamId_
1289  << " pri=" << folly::to<int>(pri);
1290  if (streamId_ == NoStream ||
1293  (streamId_ & 0x01) == 1) ||
1295  ((streamId_ & 0x1) == 0)) ||
1297  assocStream == NoStream)) {
1298  LOG(ERROR) << " invalid syn stream stream_id=" << streamId_
1299  << " lastStreamID_=" << lastStreamID_
1300  << " assocStreamID=" << assocStream
1301  << " direction=" << transportDirection_;
1302  throw SPDYSessionFailed(spdy::GOAWAY_PROTOCOL_ERROR);
1303  }
1304 
1305  if (streamId_ == lastStreamID_) {
1306  throw SPDYStreamFailed(true, streamId_, spdy::RST_PROTOCOL_ERROR);
1307  }
1308  if (callback_->numIncomingStreams() >=
1311  throw SPDYStreamFailed(true, streamId_, spdy::RST_REFUSED_STREAM);
1312  }
1313  if (assocStream != NoStream && !(flags_ & spdy::CTRL_FLAG_UNIDIRECTIONAL)) {
1314  throw SPDYStreamFailed(true, streamId_, spdy::RST_PROTOCOL_ERROR);
1315  }
1316  if (sessionClosing_ != ClosingState::CLOSING) {
1318  }
1320  StreamID(assocStream), headers, pri, size);
1321 }
1322 
1324  const HTTPHeaderSize& size) {
1325  VLOG(4) << "Got SYN_REPLY, stream=" << streamId_;
1327  (streamId_ & 0x1) == 0) {
1328  throw SPDYStreamFailed(true, streamId_, spdy::RST_PROTOCOL_ERROR);
1329  }
1330  // Server push transactions, short of any better heuristics,
1331  // should have a background priority. Thus, we pick the largest
1332  // numerical value for the SPDY priority, which no matter what
1333  // protocol version this is can be conveyed to onSynCommon by -1.
1334  onSynCommon(StreamID(streamId_), NoStream, headers, -1, size);
1335 }
1336 
1338  VLOG(4) << "Got RST_STREAM, stream=" << streamId_
1339  << ", status=" << statusCode;
1343 }
1344 
1346  VLOG(4) << "Got " << settings.size() << " settings with "
1347  << "version=" << version_ << " and flags="
1348  << std::hex << folly::to<unsigned int>(flags_) << std::dec;
1349  SettingsList settingsList;
1350  for (const SettingData& cur: settings) {
1351  // For now, we never ask for anything to be persisted, so ignore anything
1352  // coming back
1353  if (cur.flags & spdy::ID_FLAG_SETTINGS_PERSISTED) {
1354  VLOG(2) << "Ignoring bogus persisted setting: " << cur.id;
1355  continue;
1356  }
1357 
1358  switch (cur.id) {
1365  // These will be stored in ingressSettings_ and passed to the callback
1366  // but we currently ignore the PERSIST flag
1367  break;
1369  break;
1371  if (cur.value > std::numeric_limits<int32_t>::max()) {
1372  throw SPDYSessionFailed(spdy::GOAWAY_PROTOCOL_ERROR);
1373  }
1374  break;
1375  default:
1376  LOG(ERROR) << "Received unknown setting with ID=" << cur.id
1377  << ", value=" << cur.value
1378  << ", and flags=" << std::hex << cur.flags << std::dec;
1379  }
1382  auto id = spdy::spdyToHttpSettingsId((spdy::SettingsId)cur.id);
1383  if (id) {
1384  ingressSettings_.setSetting(*id, cur.value);
1385  auto s = ingressSettings_.getSetting(*id);
1386  settingsList.push_back(*s);
1387  }
1388  }
1389  }
1390  callback_->onSettings(settingsList);
1391 }
1392 
1394  bool odd = uniqueID & 0x1;
1395  bool isReply = true;
1397  if (odd) {
1398  isReply = false;
1399  }
1400  } else if (!odd) {
1401  isReply = false;
1402  }
1403 
1404  if (isReply) {
1405  if (uniqueID >= nextEgressPingID_) {
1406  LOG(INFO) << "Received reply for pingID=" << uniqueID
1407  << " that was never sent";
1408  return;
1409  }
1410  callback_->onPingReply(uniqueID);
1411  } else {
1412  callback_->onPingRequest(uniqueID);
1413  }
1414 }
1415 
1416 void SPDYCodec::onGoaway(uint32_t lastGoodStream,
1418  VLOG(4) << "Got GOAWAY, lastGoodStream=" << lastGoodStream
1419  << ", statusCode=" << statusCode;
1420 
1421  if (lastGoodStream < ingressGoawayAck_) {
1422  ingressGoawayAck_ = lastGoodStream;
1423  // Drain all streams <= lastGoodStream
1424  // and abort streams > lastGoodStream
1425  auto errorCode = ErrorCode::PROTOCOL_ERROR;
1427  errorCode = spdy::goawayToErrorCode(spdy::GoawayStatusCode(statusCode));
1428  }
1429  callback_->onGoaway(lastGoodStream, errorCode);
1430  } else {
1431  LOG(WARNING) << "Received multiple GOAWAY with increasing ack";
1432  }
1433 }
1434 
1436  VLOG(3) << "onHeaders is unimplemented.";
1437 }
1438 
1441  "onWindowUpdate", streamId_, delta);
1442 }
1443 
1445  uint32_t code, string excStr) {
1446  // Suppress any EOM callback for the current frame.
1447  if (streamID == streamId_) {
1448  flags_ &= ~spdy::CTRL_FLAG_FIN;
1449  }
1450 
1451  HTTPException err(
1452  code >= 100 ?
1455  folly::to<std::string>("SPDYCodec stream error: stream=",
1456  streamID, " status=", code, " exception: ", excStr));
1457  if (code >= 100) {
1458  err.setHttpStatusCode(code);
1459  } else {
1461  }
1463 
1464  if (partialMsg_) {
1466  }
1467  // store the ingress buffer
1468  if (currentIngressBuf_) {
1470  }
1471  callback_->onError(streamID, err, newStream);
1472 }
1473 
1475  HTTPException err(
1477  folly::to<std::string>("SPDYCodec session error: "
1478  "lastGoodStream=", lastStreamID_, " status=", code));
1481 
1482  // store the ingress buffer
1483  if (currentIngressBuf_) {
1485  }
1486  callback_->onError(0, err);
1487 }
1488 
1490  if (statusCode == 0) {
1491  // 0 is not a valid status code for RST_STREAM
1492  return false;
1493  }
1494  // SPDY/3 supports more status codes for RST_STREAM. For SPDY/2,
1495  // we just use PROTOCOL_ERROR for these new higher numbered error codes.
1496  return (versionSettings_.majorVersion != 2 ||
1497  statusCode <= spdy::RST_FLOW_CONTROL_ERROR);
1498 }
1499 
1502  // Fail fast if it's not possible for the protocol string to define a
1503  // SPDY protocol. strlen("spdy/1") == 6
1504  if (protocol.length() < 6) {
1505  return folly::none;
1506  }
1507 
1508  if (protocol == "spdy/3.1") {
1509  return SPDYVersion::SPDY3_1;
1510  }
1511  if (protocol == "spdy/3") {
1512  return SPDYVersion::SPDY3;
1513  }
1514 
1515  return folly::none;
1516 }
1517 
1518 }
HTTPSettings ingressSettings_
Definition: SPDYCodec.h:344
void onWindowUpdate(uint32_t delta) noexcept
Definition: SPDYCodec.cpp:1439
const std::string & getUserAgent() const override
Definition: SPDYCodec.cpp:206
const std::string kVersionStrv3
size_t onIngress(const folly::IOBuf &buf) override
Definition: SPDYCodec.cpp:234
Definition: test.c:42
void setHttpStatusCode(uint32_t statusCode)
Definition: HTTPException.h:86
void onSettings(const SettingList &settings)
Definition: SPDYCodec.cpp:1345
void deliverOnMessageBegin(StreamID streamID, StreamID assocStreamID, HTTPMessage *msg)
Definition: SPDYCodec.cpp:1271
void append(std::unique_ptr< folly::IOBuf > &&buf, bool pack=false)
Definition: IOBufQueue.cpp:143
flags
Definition: http_parser.h:127
~SPDYCodec() override
Definition: SPDYCodec.cpp:186
size_t chainLength() const
Definition: IOBufQueue.h:492
spdy::GoawayStatusCode statusCode
Definition: SPDYCodec.cpp:110
bool exists(folly::StringPiece name) const
Definition: HTTPHeaders.cpp:86
uint8_t getVersion() const
Definition: SPDYCodec.cpp:1023
uint32_t maxFrameLength_
Definition: SPDYCodec.h:361
virtual void onError(StreamID stream, const HTTPException &error, bool newTxn=false)=0
void setCurrentIngressBuf(std::unique_ptr< folly::IOBuf > buf)
virtual void onMessageBegin(StreamID stream, HTTPMessage *msg)=0
std::string str() const
Definition: Range.h:591
std::unique_ptr< folly::IOBuf > serializeResponseHeaders(const HTTPMessage &msg, uint32_t headroom=0, HTTPHeaderSize *size=nullptr)
Definition: SPDYCodec.cpp:557
static const StreamID MAX_STREAM_ID
Definition: HTTPCodec.h:55
bool caseInsensitiveEqual(folly::StringPiece s, folly::StringPiece t)
Definition: UtilInl.h:17
void checkLength(uint32_t expectedLength, const std::string &msg)
Definition: SPDYCodec.cpp:219
uint32_t streamId_
Definition: SPDYCodec.h:362
bool deliverCallbackIfAllowed(T callbackFn, char const *cbName, StreamID stream, Args &&...args)
LogLevel max
Definition: LogLevel.cpp:31
auto add
Definition: BaseTest.cpp:70
void onSynStream(uint32_t assocStream, uint8_t pri, uint8_t slot, const compress::HeaderPieceList &headers, const HTTPHeaderSize &size)
Definition: SPDYCodec.cpp:1283
virtual void onSettings(const SettingsList &)
Definition: HTTPCodec.h:251
const folly::IOBuf * currentIngressBuf_
Definition: SPDYCodec.h:355
const std::string & getStatusMessage() const
Definition: HTTPMessage.h:245
#define VERSION_MASK
Definition: SPDYCodec.cpp:67
HTTPCodec::Callback * callback_
static bool validateHeaderName(folly::ByteRange name)
Definition: CodecUtil.h:43
StreamCodecFactory stream
virtual void onMessageComplete(StreamID stream, bool upgrade)=0
void clone(std::unique_ptr< folly::IOBuf > &buf, size_t len)
Definition: Cursor.h:456
const std::string kSessionProtoNameSPDY3
uint16_t getStatusCode() const
virtual void onPingReply(uint64_t)
Definition: HTTPCodec.h:237
std::unique_ptr< HTTPMessage > partialMsg_
Definition: SPDYCodec.h:353
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
#define LIKELY(x)
Definition: Likely.h:47
bool isSharedOne() const
Definition: IOBuf.h:952
size_t tailroom() const
Definition: IOBufQueue.h:403
const std::string kNamePathv3
void setPartialMsg(std::unique_ptr< HTTPMessage > partialMsg)
void setMaxFrameLength(uint32_t maxFrameLength)
Definition: SPDYCodec.cpp:189
std::vector< SettingData > SettingList
Definition: SPDYCodec.h:153
const uint8_t * data() const
Definition: IOBuf.h:499
const std::string & protocolVersionString
void setProxygenError(ProxygenError proxygenError)
Definition: Exception.h:46
void onRstStream(uint32_t statusCode) noexcept
Definition: SPDYCodec.cpp:1337
static std::string formatDateHeader()
const unsigned char kSPDYv3Dictionary[]
std::string userAgent_
Definition: SPDYCodec.h:354
bool supportsSessionFlowControl() const override
Definition: SPDYCodec.cpp:214
static http_parser_settings settings
Definition: test.c:1529
#define FLAGS_MASK
Definition: SPDYCodec.cpp:65
size_t generateRstStream(folly::IOBufQueue &writeBuf, StreamID txn, ErrorCode statusCode) override
Definition: SPDYCodec.cpp:824
void setCodecStatusCode(ErrorCode statusCode)
Definition: HTTPException.h:98
std::unique_ptr< IOBuf > clone() const
Definition: IOBuf.cpp:527
auto odd
Definition: CombineTest.cpp:35
uint8_t getPriority() const
Definition: HTTPMessage.h:590
requires E e noexcept(noexcept(s.error(std::move(e))))
size_t parseIngress(const folly::IOBuf &buf)
Definition: SPDYCodec.cpp:246
std::unique_ptr< folly::IOBuf > serializeRequestHeaders(const HTTPMessage &msg, bool isPushed, uint32_t headroom=0, HTTPHeaderSize *size=nullptr)
Definition: SPDYCodec.cpp:587
tuple make_tuple()
Definition: gtest-tuple.h:675
ResetStatusCode errorCodeToReset(ErrorCode code)
void onControlFrame(folly::io::Cursor &cursor)
Definition: SPDYCodec.cpp:335
const std::string kVersionStrv31
string date
static bool validateMethod(folly::ByteRange method)
Definition: CodecUtil.h:33
#define FRAME_HEADER_LEN
Definition: SPDYCodec.cpp:71
std::size_t getNumSettings() const
Definition: HTTPSettings.h:58
void writeLE(T value)
Definition: Cursor.h:750
std::pair< const uint8_t *, size_t > peek()
Definition: Cursor.h:451
static const StreamID NoStream
Definition: SPDYCodec.h:38
void setEncodeHeadroom(uint32_t headroom)
Definition: HeaderCodec.h:69
size_t generateChunkHeader(folly::IOBufQueue &writeBuf, StreamID stream, size_t length) override
Definition: SPDYCodec.cpp:790
std::unique_ptr< folly::IOBuf > encodeHeaders(const HTTPMessage &msg, std::vector< compress::Header > &headers, uint32_t headroom=0, HTTPHeaderSize *size=nullptr)
Definition: SPDYCodec.cpp:503
const std::string kNameMethodv2
ProtocolVersion version
size_t generatePingReply(folly::IOBufQueue &writeBuf, uint64_t uniqueID) override
Definition: SPDYCodec.cpp:924
size_t generateEOM(folly::IOBufQueue &writeBuf, StreamID stream) override
Definition: SPDYCodec.cpp:812
const char * name
Definition: http_parser.c:437
void set(folly::StringPiece name, const std::string &value)
Definition: HTTPHeaders.h:119
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
size_t generateBody(folly::IOBufQueue &writeBuf, StreamID stream, std::unique_ptr< folly::IOBuf > chain, folly::Optional< uint8_t > padding, bool eom) override
Definition: SPDYCodec.cpp:765
void onSynCommon(StreamID streamID, StreamID assocStreamID, const compress::HeaderPieceList &headers, int8_t pri, const HTTPHeaderSize &size)
Definition: SPDYCodec.cpp:1238
bool isNew
Definition: SPDYCodec.cpp:130
enum proxygen::HTTPParallelCodec::ClosingState sessionClosing_
virtual void onPingRequest(uint64_t)
Definition: HTTPCodec.h:230
void writeBuf(const Buf &buf, folly::io::Appender &out)
virtual uint32_t numIncomingStreams() const
Definition: HTTPCodec.h:327
std::unique_ptr< IOBuf > pop()
Definition: IOBuf.h:859
LogLevel min
Definition: LogLevel.cpp:30
const std::string kNameStatusv2
void prepend(std::size_t amount)
Definition: IOBuf.h:673
void onSynReply(const compress::HeaderPieceList &headers, const HTTPHeaderSize &size)
Definition: SPDYCodec.cpp:1323
virtual void addPriorityNode(StreamID id, StreamID parent)=0
static bool validateURL(folly::ByteRange url)
Definition: CodecUtil.h:29
std::deque< HeaderPiece > HeaderPieceList
Definition: HeaderPiece.h:59
ErrorCode goawayToErrorCode(GoawayStatusCode code)
#define CTRL_MASK
Definition: SPDYCodec.cpp:64
std::size_t headroom() const
Definition: IOBuf.h:542
virtual void onFrameHeader(StreamID, uint8_t, uint64_t, uint8_t, uint16_t=0)
Definition: HTTPCodec.h:206
const std::string kNameSchemev2
std::size_t length() const
Definition: IOBuf.h:533
void generateSynStream(StreamID stream, StreamID assocStream, folly::IOBufQueue &writeBuf, const HTTPMessage &msg, bool eom, HTTPHeaderSize *size)
Definition: SPDYCodec.cpp:665
const std::string kNameMethodv3
folly::Optional< proxygen::SettingsId > spdyToHttpSettingsId(proxygen::spdy::SettingsId id)
bool supportsStreamFlowControl() const override
Definition: SPDYCodec.cpp:210
std::unique_ptr< HTTPMessage > parseHeaders(TransportDirection direction, StreamID streamID, StreamID assocStreamID, const compress::HeaderPieceList &headers)
Definition: SPDYCodec.cpp:1059
bool rstStatusSupported(int statusCode) const
Definition: SPDYCodec.cpp:1489
const char * getErrorCodeString(ErrorCode error)
Definition: ErrorCode.cpp:18
std::string message
Definition: SPDYCodec.cpp:133
const std::string kNameVersionv3
void retreat(std::size_t amount)
Definition: IOBuf.h:653
void generatePushPromise(folly::IOBufQueue &writeBuf, StreamID stream, const HTTPMessage &msg, StreamID assocstream, bool eom=false, HTTPHeaderSize *size=nullptr) override
Definition: SPDYCodec.cpp:646
GoawayStatusCode errorCodeToGoaway(ErrorCode code)
static const char *const value
Definition: Conv.cpp:50
bool isStreamIngressEgressAllowed(StreamID stream) const
HTTPHeaders & getHeaders()
Definition: HTTPMessage.h:273
#define DELTA_WINDOW_SIZE_MASK
Definition: SPDYCodec.cpp:68
const std::string & getSingleOrEmpty(const T &nameOrCode) const
Definition: HTTPHeaders.h:420
size_t generateTrailers(folly::IOBufQueue &writeBuf, StreamID stream, const HTTPHeaders &trailers) override
Definition: SPDYCodec.cpp:803
const std::string kNameHostv3
const std::string httpVersion
RWCursor< CursorAccess::PRIVATE > RWPrivateCursor
Definition: Cursor.h:958
StreamID nextEgressPingID_
Definition: SPDYCodec.h:357
const std::string & getPushStatusStr() const
const std::string kNamePathv2
std::size_t computeChainDataLength() const
Definition: IOBuf.cpp:501
size_t size() const
#define STREAM_ID_MASK
Definition: SPDYCodec.cpp:66
virtual void setMaxUncompressed(uint64_t maxUncompressed)
Definition: HeaderCodec.h:73
size_t generatePingCommon(folly::IOBufQueue &writeBuf, uint64_t uniqueID)
Definition: SPDYCodec.cpp:929
const std::string kNameStatusv3
uint8_t getMinorVersion() const
Definition: SPDYCodec.cpp:1027
folly::Optional< proxygen::spdy::SettingsId > httpToSpdySettingsId(proxygen::SettingsId id)
virtual void onHeadersComplete(StreamID stream, std::unique_ptr< HTTPMessage > msg)=0
size_t generateDataFrame(folly::IOBufQueue &writeBuf, uint32_t streamID, uint8_t flags, uint32_t length, std::unique_ptr< folly::IOBuf > payload)
Definition: SPDYCodec.cpp:1031
const std::string empty_string
Definition: HTTPHeaders.cpp:23
const std::string kNameVersionv2
std::vector< HTTPSetting > SettingsList
Definition: HTTPSettings.h:81
virtual void onGoaway(uint64_t, ErrorCode, std::unique_ptr< folly::IOBuf >=nullptr)
Definition: HTTPCodec.h:220
size_t addPriorityNodes(PriorityQueue &queue, folly::IOBufQueue &writeBuf, uint8_t maxLevel) override
Definition: SPDYCodec.cpp:1010
const std::vector< HTTPSetting > & getAllSettings()
Definition: HTTPSettings.h:61
virtual void onAbort(StreamID, ErrorCode)
Definition: HTTPCodec.h:192
void failStream(bool newTxn, StreamID streamID, uint32_t code, std::string excStr=empty_string)
Definition: SPDYCodec.cpp:1444
void skip(size_t len)
Definition: Cursor.h:371
const char * string
Definition: Conv.cpp:212
size_t generateWindowUpdate(folly::IOBufQueue &writeBuf, StreamID stream, uint32_t delta) override
Definition: SPDYCodec.cpp:981
static set< string > s
const std::string & getMethodString() const
const std::string kNameSchemev3
uint64_t StreamID
Definition: HTTPCodec.h:49
bool isSecure() const
Definition: HTTPMessage.h:535
GzipHeaderCodec headerCodec_
Definition: SPDYCodec.h:377
void add(folly::StringPiece name, folly::StringPiece value)
Definition: HTTPHeaders.cpp:52
HeaderDecodeResult decodeHeaders(folly::io::Cursor &cursor)
Definition: SPDYCodec.cpp:466
static bool validateHeaderValue(folly::ByteRange value, CtlEscapeMode mode)
Definition: CodecUtil.h:66
std::unique_ptr< folly::IOBuf > encode(std::vector< compress::Header > &headers) noexcept
virtual void onWindowUpdate(StreamID, uint32_t)
Definition: HTTPCodec.h:243
size_t generateChunkTerminator(folly::IOBufQueue &writeBuf, StreamID stream) override
Definition: SPDYCodec.cpp:797
SPDYCodec(TransportDirection direction, SPDYVersion version, int spdyCompressionLevel=Z_NO_COMPRESSION)
Definition: SPDYCodec.cpp:170
const uint32_t kMaxConcurrentStreams
virtual void onPushMessageBegin(StreamID, StreamID, HTTPMessage *)
Definition: HTTPCodec.h:95
size_t generateSettings(folly::IOBufQueue &writeBuf) override
Definition: SPDYCodec.cpp:941
const std::string & getURL() const
Definition: HTTPMessage.h:205
TransportDirection transportDirection_
static const SPDYVersionSettings & getVersionSettings(SPDYVersion version)
Definition: SPDYCodec.cpp:138
folly::Expected< HeaderDecodeResult, GzipDecodeError > decode(folly::io::Cursor &cursor, uint32_t length) noexcept
void postallocate(std::size_t n)
Definition: IOBufQueue.h:380
void trimEnd(std::size_t amount)
Definition: IOBuf.h:718
void failSession(uint32_t statusCode)
Definition: SPDYCodec.cpp:1474
void generateHeader(folly::IOBufQueue &writeBuf, StreamID stream, const HTTPMessage &msg, bool eom=false, HTTPHeaderSize *size=nullptr) override
Definition: SPDYCodec.cpp:625
const SPDYVersionSettings & versionSettings_
Definition: SPDYCodec.h:342
virtual void onBody(StreamID stream, std::unique_ptr< folly::IOBuf > chain, uint16_t padding)=0
const HTTPHeaderSize & getDecodedSize()
uint32_t streamID
Definition: SPDYCodec.cpp:131
size_t generatePingRequest(folly::IOBufQueue &writeBuf) override
Definition: SPDYCodec.cpp:917
static constexpr uint64_t data[1]
Definition: Fingerprint.cpp:43
const HTTPHeaderSize & getEncodedSize()
Definition: HeaderCodec.h:62
bool isSPDYReserved(const std::string &name)
Definition: SPDYCodec.cpp:489
void checkMinLength(uint32_t minLength, const std::string &msg)
Definition: SPDYCodec.cpp:227
CodecProtocol getProtocol() const override
Definition: SPDYCodec.cpp:197
HTTPSettings egressSettings_
Definition: SPDYCodec.h:348
void onPing(uint32_t uniqueID) noexcept
Definition: SPDYCodec.cpp:1393
size_t totalLength() const
Definition: Cursor.h:126
void forEachWithCode(LAMBDA func) const
Definition: HTTPHeaders.h:346
void onHeaders(const compress::HeaderPieceList &headers) noexcept
Definition: SPDYCodec.cpp:1435
folly::Function< void()> parent
Definition: AtFork.cpp:34
ErrorCode rstToErrorCode(uint32_t code)
constexpr None none
Definition: Optional.h:87
enum proxygen::SPDYCodec::FrameState frameState_
const HTTPSetting * getSetting(SettingsId id) const
void onGoaway(uint32_t lastGoodStream, uint32_t statusCode) noexcept
Definition: SPDYCodec.cpp:1416
size_t generateGoaway(folly::IOBufQueue &writeBuf, StreamID lastStream, ErrorCode statusCode, std::unique_ptr< folly::IOBuf > debugData=nullptr) override
Definition: SPDYCodec.cpp:856
static bool hasGzipAndDeflate(const std::string &value, bool &hasGzip, bool &hasDeflate)
Definition: CodecUtil.cpp:61
void generateSynReply(StreamID stream, folly::IOBufQueue &writeBuf, const HTTPMessage &msg, bool eom, HTTPHeaderSize *size)
Definition: SPDYCodec.cpp:724
void setSetting(SettingsId id, SettingsValue val)
void writeBE(T value)
Definition: Cursor.h:744
void setMaxUncompressedHeaders(uint32_t maxUncompressed)
Definition: SPDYCodec.cpp:193