Fetch API
主要暴露了三个接口一个方法。
- 三个接口
Request
(资源请求)Response
(请求的响应)Headers
(Request/Response
头部信息)
- 一个方法
fetch()
(获取资源调用的方法- 更多介绍参考 Fetch API - Web API | MDN (mozilla.org)
一、 来看一段前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test fetch</title>
<script>
function Test() {
fetch("http://192.168.8.1/chfs/shared/test/format.1728106021784.json")
.then((response) => response.json())
.then((data) => console.log(data));
}
</script>
</head>
<body>
<button onclick="Test()">Test fetch</button>
</body>
</html>
二、看c++代码对fetch response具体实现
1、services\network\public\mojom\fetch_api.mojom
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
module network.mojom;
// Corresponds to Fetch request's "mode" and "use-CORS-preflight flag":
// https://fetch.spec.whatwg.org/#concept-request-mode
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum RequestMode {
kSameOrigin = 0,
kNoCors = 1,
kCors = 2,
kCorsWithForcedPreflight = 3,
kNavigate = 4,
// Add a new type here, then update "FetchRequestMode" in enums.xml.
};
// Corresponds to Fetch request's "destination":
// https://fetch.spec.whatwg.org/#concept-request-destination
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum RequestDestination {
kEmpty = 0,
kAudio = 1,
kAudioWorklet = 2,
// kDocument is for a main resource request in a main frame, or a Portal.
kDocument = 3,
kEmbed = 4,
kFont = 5,
kFrame = 6,
kIframe = 7,
kImage = 8,
kManifest = 9,
kObject = 10,
kPaintWorklet = 11,
kReport = 12,
kScript = 13,
kServiceWorker = 14,
kSharedWorker = 15,
kStyle = 16,
kTrack = 17,
kVideo = 18,
// kWebBundle represents a request for a WebBundle. A <script> element whose
// type is "webbundle" uses this destination.
//
// e.g. <script type=webbundle> { "source": "foo.wbn", ... } </script>
//
// Fetch specifiction does not define this destination yet.
// Tracking issue: https://github.com/whatwg/fetch/issues/1120
kWebBundle = 19,
kWorker = 20,
kXslt = 21,
// kFencedframe represents a main resource request in a fenced frame. A
// <fencedframe> element uses this destination.
//
// e.g. <fencedframe src="example.com"></fencedframe>
//
// Fenced Frame is not standardized yet. See
// https://github.com/shivanigithub/fenced-frame for the explainer and
// crbug.com/1123606 for the implementation.
kFencedframe = 22,
// Requests from the federated credential management API,
// https://fedidcg.github.io/FedCM/
kWebIdentity = 23,
// Requests for compression dictionary
kDictionary = 24,
// Requests for speculation rules.
// https://wicg.github.io/nav-speculation/speculation-rules.html
kSpeculationRules = 25,
};
// Corresponds to Fetch request's "redirect mode":
// https://fetch.spec.whatwg.org/#concept-request-redirect-mode
enum RedirectMode {
kFollow,
kError,
kManual,
};
// Corresponds to Fetch request's "credentials mode":
// https://fetch.spec.whatwg.org/#concept-request-credentials-mode
enum CredentialsMode {
// TODO(https://crbug.com/775438): Due to a bug, this does not properly
// correspond to Fetch's "credentials mode", in that client certificates will
// be sent is available, or the handshake will be aborted in order to allow
// selecting a client cert. The correct behavior is to omit all client certs
// and continue the handshake without sending one if requested.
kOmit,
kSameOrigin,
kInclude,
// TODO(https://crbug.com/775438): This works around kOmit not doing the
// spec-defined behavior. This is a temporary workaround that explicitly
// indicates the caller wants the spec-defined behavior. It's named as such
// because this should be only temporary, until kOmit is fixed.
kOmitBug_775438_Workaround
};
// Corresponds to response types from the Fetch spec:
// https://fetch.spec.whatwg.org/#concept-response-type
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum FetchResponseType {
kBasic = 0,
kCors = 1,
kDefault = 2,
kError = 3,
kOpaque = 4,
kOpaqueRedirect = 5,
// Add a new type here, then update "FetchResponseType" in enums.xml.
};
// Indicates the source of a response.
// This represents the source of the outmost response of a request.
// This is used only for histograms and isn't web-exposed.
enum FetchResponseSource {
// The source is unspecified: e.g. "new Response('hi')" or a response from
// a service worker.
kUnspecified,
// The response came from network: e.g. "fetch(req)".
kNetwork,
// The response came from HttpCache: e.g. "fetch(req)" and there is an entry
// in HttpCache.
kHttpCache,
// The response came from CacheStorage: e.g. "cache.match(req)" in a fetch
// event handler.
kCacheStorage,
};
2、在third_party\blink\renderer\core\fetch\global_fetch.h 定义了Fetch函数具体实现
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_GLOBAL_FETCH_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_GLOBAL_FETCH_H_
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/fetch/request.h"
namespace blink {
class ExceptionState;
class LocalDOMWindow;
class NavigatorBase;
class RequestInit;
class DeferredRequestInit;
class ScriptState;
class WorkerGlobalScope;
class FetchLaterResult;
class CORE_EXPORT GlobalFetch {
STATIC_ONLY(GlobalFetch);
public:
class CORE_EXPORT ScopedFetcher : public GarbageCollectedMixin {
public:
virtual ~ScopedFetcher();
virtual ScriptPromise Fetch(ScriptState*,
const V8RequestInfo*,
const RequestInit*,
ExceptionState&) = 0;
virtual FetchLaterResult* FetchLater(ScriptState*,
const V8RequestInfo*,
const DeferredRequestInit*,
ExceptionState&);
// Returns the number of fetch() method calls in the associated execution
// context. This is used for metrics.
virtual uint32_t FetchCount() const = 0;
static ScopedFetcher* From(LocalDOMWindow&);
static ScopedFetcher* From(WorkerGlobalScope&);
static ScopedFetcher* From(NavigatorBase& navigator);
void Trace(Visitor*) const override;
};
static ScriptPromise fetch(ScriptState* script_state,
LocalDOMWindow& window,
const V8RequestInfo* input,
const RequestInit* init,
ExceptionState& exception_state);
static ScriptPromise fetch(ScriptState* script_state,
WorkerGlobalScope& worker,
const V8RequestInfo* input,
const RequestInit* init,
ExceptionState& exception_state);
static FetchLaterResult* fetchLater(ScriptState* script_state,
LocalDOMWindow& window,
const V8RequestInfo* input,
const DeferredRequestInit* init,
ExceptionState& exception_state);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_GLOBAL_FETCH_H_
3、Fetch 管理类 third_party\blink\renderer\core\fetch\fetch_manager.h
4、前端接口 Request
(资源请求) third_party\blink\renderer\core\fetch\headers.idl
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// https://fetch.spec.whatwg.org/#typedefdef-headersinit
typedef (sequence<sequence<ByteString>> or record<ByteString, ByteString>) HeadersInit;
// https://fetch.spec.whatwg.org/#headers-class
[
Exposed=(Window,Worker)
] interface Headers {
[CallWith=ScriptState, RaisesException] constructor(optional HeadersInit init);
[CallWith=ScriptState, RaisesException] void append(ByteString name, ByteString value);
[CallWith=ScriptState, ImplementedAs=remove, RaisesException] void delete(ByteString key);
[RaisesException] ByteString? get(ByteString key);
sequence<ByteString> getSetCookie();
[RaisesException] boolean has(ByteString key);
[CallWith=ScriptState, RaisesException] void set(ByteString key, ByteString value);
iterable<ByteString, ByteString>;
};
对应实现c++类:
third_party\blink\renderer\core\fetch\headers.h
third_party\blink\renderer\core\fetch\headers.cc
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_HEADERS_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_HEADERS_H_
#include "third_party/blink/renderer/bindings/core/v8/iterable.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_headers.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/fetch/fetch_header_list.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
namespace blink {
class ExceptionState;
class ScriptState;
// http://fetch.spec.whatwg.org/#headers-class
class CORE_EXPORT Headers final : public ScriptWrappable,
public PairSyncIterable<Headers> {
DEFINE_WRAPPERTYPEINFO();
public:
enum Guard {
kImmutableGuard,
kRequestGuard,
kRequestNoCorsGuard,
kResponseGuard,
kNoneGuard
};
static Headers* Create(ScriptState* script_state,
ExceptionState& exception_state);
static Headers* Create(ScriptState* script_state,
const V8HeadersInit* init,
ExceptionState& exception_state);
// Shares the FetchHeaderList. Called when creating a Request or Response.
static Headers* Create(FetchHeaderList*);
Headers();
// Shares the FetchHeaderList. Called when creating a Request or Response.
explicit Headers(FetchHeaderList*);
Headers* Clone() const;
// Headers.idl implementation.
void append(ScriptState* script_state,
const String& name,
const String& value,
ExceptionState&);
void remove(ScriptState* script_state, const String& key, ExceptionState&);
String get(const String& key, ExceptionState&);
Vector<String> getSetCookie();
bool has(const String& key, ExceptionState&);
void set(ScriptState* script_state,
const String& key,
const String& value,
ExceptionState&);
void SetGuard(Guard guard) { guard_ = guard; }
Guard GetGuard() const { return guard_; }
// These methods should only be called when size() would return 0.
void FillWith(ScriptState* script_state, const Headers*, ExceptionState&);
void FillWith(ScriptState* script_state,
const V8HeadersInit* init,
ExceptionState& exception_state);
// https://fetch.spec.whatwg.org/#concept-headers-remove-privileged-no-cors-request-headers
void RemovePrivilegedNoCorsRequestHeaders();
FetchHeaderList* HeaderList() const { return header_list_.Get(); }
void Trace(Visitor*) const override;
private:
class HeadersIterationSource final
: public PairSyncIterable<Headers>::IterationSource {
public:
explicit HeadersIterationSource(Headers* headers);
~HeadersIterationSource() override;
bool FetchNextItem(ScriptState* script_state,
String& key,
String& value,
ExceptionState& exception) override;
void Trace(Visitor*) const override;
void ResetHeaderList();
private:
// https://webidl.spec.whatwg.org/#dfn-value-pairs-to-iterate-over
Vector<std::pair<String, String>> headers_list_;
// https://webidl.spec.whatwg.org/#default-iterator-object-index
wtf_size_t current_ = 0;
Member<Headers> headers_;
};
// These methods should only be called when size() would return 0.
void FillWith(ScriptState* script_state,
const Vector<Vector<String>>&,
ExceptionState&);
void FillWith(ScriptState* script_state,
const Vector<std::pair<String, String>>&,
ExceptionState&);
Member<FetchHeaderList> header_list_;
Guard guard_;
IterationSource* CreateIterationSource(ScriptState*,
ExceptionState&) override;
HeapHashSet<WeakMember<HeadersIterationSource>> iterators_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_HEADERS_H_
5、Response
(请求的响应) third_party\blink\renderer\core\fetch\response.idl
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// https://fetch.spec.whatwg.org/#response-class
enum ResponseType { "basic", "cors", "default", "error", "opaque", "opaqueredirect" };
[
Exposed=(Window,Worker)
] interface Response {
// TODO(yhirano): We use "any" for body because the IDL processor doesn't
// recognize ReadableStream implemented with V8 extras. Fix it.
[CallWith=ScriptState, RaisesException] constructor(optional any body, optional ResponseInit init = {});
[CallWith=ScriptState, NewObject] static Response error();
[CallWith=ScriptState, NewObject, RaisesException] static Response redirect(USVString url, optional unsigned short status = 302);
[CallWith=ScriptState, NewObject, RaisesException, ImplementedAs=staticJson] static Response json(any data, optional ResponseInit init = {});
readonly attribute ResponseType type;
readonly attribute USVString url;
readonly attribute boolean redirected;
readonly attribute unsigned short status;
readonly attribute boolean ok;
readonly attribute ByteString statusText;
[SameObject] readonly attribute Headers headers;
[RaisesException, CallWith=ScriptState, NewObject] Response clone();
[Affects=Everything, MeasureAs=FetchBodyStream] readonly attribute ReadableStream? body;
};
Response includes Body;
对应response接口c++实现类:
third_party\blink\renderer\core\fetch\response.h
third_party\blink\renderer\core\fetch\response.cc
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_RESPONSE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_RESPONSE_H_
#include "services/network/public/mojom/fetch_api.mojom-blink.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_response.mojom-blink-forward.h"
#include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/fetch/body.h"
#include "third_party/blink/renderer/core/fetch/body_stream_buffer.h"
#include "third_party/blink/renderer/core/fetch/fetch_response_data.h"
#include "third_party/blink/renderer/core/fetch/headers.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/blob/blob_data.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
class ExceptionState;
class ResponseInit;
class ScriptState;
class CORE_EXPORT Response final : public ScriptWrappable, public Body {
DEFINE_WRAPPERTYPEINFO();
public:
// These "create" function which takes a ScriptState* must be called with
// entering an appropriate V8 context.
// From Response.idl:
static Response* Create(ScriptState*, ExceptionState&);
static Response* Create(ScriptState*,
ScriptValue body,
const ResponseInit*,
ExceptionState&);
static Response* Create(ScriptState*,
BodyStreamBuffer*,
const String& content_type,
const ResponseInit*,
ExceptionState&);
static Response* Create(ExecutionContext*, FetchResponseData*);
static Response* Create(ScriptState*, mojom::blink::FetchAPIResponse&);
static Response* CreateClone(const Response&);
static Response* error(ScriptState*);
static Response* redirect(ScriptState*,
const String& url,
uint16_t status,
ExceptionState&);
static Response* staticJson(ScriptState*,
ScriptValue data,
const ResponseInit*,
ExceptionState&);
static FetchResponseData* CreateUnfilteredFetchResponseDataWithoutBody(
ScriptState*,
mojom::blink::FetchAPIResponse&);
static FetchResponseData* FilterResponseData(
network::mojom::FetchResponseType response_type,
FetchResponseData* response,
WTF::Vector<WTF::String>& headers);
explicit Response(ExecutionContext*);
Response(ExecutionContext*, FetchResponseData*);
Response(ExecutionContext*, FetchResponseData*, Headers*);
Response(const Response&) = delete;
Response& operator=(const Response&) = delete;
const FetchResponseData* GetResponse() const { return response_.Get(); }
// From Response.idl:
String type() const;
String url() const;
bool redirected() const;
uint16_t status() const;
bool ok() const;
String statusText() const;
Headers* headers() const;
// From Response.idl:
// This function must be called with entering an appropriate V8 context.
Response* clone(ScriptState*, ExceptionState&);
// Does not contain the blob response body or any side data blob.
// |request_url| is the current request URL that resulted in the response. It
// is needed to process some response headers (e.g. CSP).
// TODO(lfg, kinuko): The FetchResponseData::url_list_ should include the
// request URL per step 9 in Main Fetch
// https://fetch.spec.whatwg.org/#main-fetch. Just fixing it might break the
// logic in ResourceMultiBufferDataProvider, please see
// https://chromium-review.googlesource.com/c/1366464 for more details.
mojom::blink::FetchAPIResponsePtr PopulateFetchAPIResponse(
const KURL& request_url);
bool HasBody() const;
BodyStreamBuffer* BodyBuffer() override { return response_->Buffer(); }
// Returns the BodyStreamBuffer of |m_response|. This method doesn't check
// the internal response of |m_response| even if |m_response| has it.
const BodyStreamBuffer* BodyBuffer() const override {
return response_->Buffer();
}
// Returns the BodyStreamBuffer of the internal response of |m_response| if
// any. Otherwise, returns one of |m_response|.
BodyStreamBuffer* InternalBodyBuffer() { return response_->InternalBuffer(); }
const BodyStreamBuffer* InternalBodyBuffer() const {
return response_->InternalBuffer();
}
bool IsBodyUsed() const override;
String ContentType() const override;
String MimeType() const override;
String InternalMIMEType() const;
const Vector<KURL>& InternalURLList() const;
FetchHeaderList* InternalHeaderList() const;
void Trace(Visitor*) const override;
private:
const Member<FetchResponseData> response_;
const Member<Headers> headers_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_RESPONSE_H_
1)、 Response body对应实现类:third_party\blink\renderer\core\fetch\body.idl
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// https://fetch.spec.whatwg.org/#body
[
ActiveScriptWrappable
] interface mixin Body {
readonly attribute boolean bodyUsed;
[CallWith=ScriptState, NewObject, RaisesException] Promise<ArrayBuffer> arrayBuffer();
[CallWith=ScriptState, NewObject, RaisesException] Promise<Blob> blob();
[CallWith=ScriptState, NewObject, RaisesException] Promise<FormData> formData();
[CallWith=ScriptState, NewObject, RaisesException] Promise<any> json();
[CallWith=ScriptState, NewObject, RaisesException] Promise<USVString> text();
// body attribute is defined in sub-interfaces, because the IDL processor
// cannot deal with attribute inheritance with runtime enabled flag.
// [RuntimeEnabled=ExperimentalStream] readonly attribute ReadableByteStream body;
};
third_party\blink\renderer\core\fetch\body.h
third_party\blink\renderer\core\fetch\body.cc
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BODY_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BODY_H_
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
class BodyStreamBuffer;
class ExceptionState;
class ExecutionContext;
class ReadableStream;
class ScriptPromiseResolver;
class ScriptState;
// This class represents Body mix-in defined in the fetch spec
// https://fetch.spec.whatwg.org/#body-mixin.
//
// Note: This class has body stream and its predicate whereas in the current
// spec only Response has it and Request has a byte stream defined in the
// Encoding spec. The spec should be fixed shortly to be aligned with this
// implementation.
class CORE_EXPORT Body : public ExecutionContextClient {
public:
explicit Body(ExecutionContext*);
Body(const Body&) = delete;
Body& operator=(const Body&) = delete;
ScriptPromise arrayBuffer(ScriptState*, ExceptionState&);
ScriptPromise blob(ScriptState*, ExceptionState&);
ScriptPromise formData(ScriptState*, ExceptionState&);
ScriptPromise json(ScriptState*, ExceptionState&);
ScriptPromise text(ScriptState*, ExceptionState&);
ReadableStream* body();
virtual BodyStreamBuffer* BodyBuffer() = 0;
virtual const BodyStreamBuffer* BodyBuffer() const = 0;
// This should only be called from the generated bindings. All other code
// should use IsBodyUsed() instead.
bool bodyUsed() const { return IsBodyUsed(); }
// True if the body has been read from.
virtual bool IsBodyUsed() const;
// True if the body is locked.
bool IsBodyLocked() const;
private:
// TODO(e_hakkinen): Fix |MimeType()| to always contain parameters and
// remove |ContentType()|.
virtual String ContentType() const = 0;
virtual String MimeType() const = 0;
// Body consumption algorithms will reject with a TypeError in a number of
// error conditions. This method wraps those up into one call which throws
// an exception if consumption cannot proceed. The caller must check
// |exception_state| on return.
void RejectInvalidConsumption(ExceptionState& exception_state) const;
// The parts of LoadAndConvertBody() that do not depend on the template
// parameters are split into this method to reduce binary size. Returns a
// freshly-created ScriptPromiseResolver* on success, or nullptr on error. On
// error, LoadAndConvertBody() must not continue.
ScriptPromiseResolver* PrepareToLoadBody(ScriptState*, ExceptionState&);
// Common implementation for body-reading accessors. To maximise performance
// at the cost of code size, this is templated on the types of the lambdas
// that are passed in.
template <class Consumer,
typename CreateLoaderFunction,
typename OnNoBodyFunction>
ScriptPromise LoadAndConvertBody(ScriptState*,
CreateLoaderFunction,
OnNoBodyFunction,
ExceptionState&);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BODY_H_
6、Headers
(Request/Response
头部信息) third_party\blink\renderer\core\fetch\headers.idl
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// https://fetch.spec.whatwg.org/#typedefdef-headersinit
typedef (sequence<sequence<ByteString>> or record<ByteString, ByteString>) HeadersInit;
// https://fetch.spec.whatwg.org/#headers-class
[
Exposed=(Window,Worker)
] interface Headers {
[CallWith=ScriptState, RaisesException] constructor(optional HeadersInit init);
[CallWith=ScriptState, RaisesException] void append(ByteString name, ByteString value);
[CallWith=ScriptState, ImplementedAs=remove, RaisesException] void delete(ByteString key);
[RaisesException] ByteString? get(ByteString key);
sequence<ByteString> getSetCookie();
[RaisesException] boolean has(ByteString key);
[CallWith=ScriptState, RaisesException] void set(ByteString key, ByteString value);
iterable<ByteString, ByteString>;
};
至此定义实现已经介绍完毕,在下一篇看下调用堆栈: