Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(197)

Side by Side Diff: net/docs/life-of-a-url-request.md

Issue 1211003003: net: Add Life of a URLRequest documentation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Response to Helen's comments Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | net/net.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Life of a URLRequest
2
3 This document is intended as an overview of the core layers of the network
4 stack, their basic responsibilities, how they fit together, and where some of
5 the pain points are, without going into too much detail. Though it touches a
6 bit on child processes and the content/loader stack, the focus is on net/
7 itself.
8
9 It's particularly targeted at people new to the Chrome network stack, but
10 should also be useful for team members who may be experts at some parts of the
11 stack, but are largely unfamiliar with other components. It starts by walking
12 through how a basic request issued by another process works its way through the
13 network stack, and then moves on to discuss how various components plug in.
14
15 If you notice any inaccuracies in this document, or feel that things could be
16 better explained, please do not hesitate to submit patches.
17
18 # Anatomy of the Network Stack
19
20 The top-level network stack object is the URLRequextContext. The context has
21 non-owning pointers to everything needed to create and issue a URLRequest. The
22 context must outlive all requests that use it. Creating a context is a rather
23 complicated process, and it's recommended that most consumers use
24 URLRequestContextBuilder to do this.
25
26 Chrome has a number of different URLRequestContexts, as there is often a need to
27 keep cookies, caches, and socket pools separate for different types of requests.
28 Here are the ones that the network team owns:
29
30 * The proxy URLRequestContext, owned by the IOThread and used to get PAC
31 scripts while avoiding re-entrancy.
32 * The system URLRequestContext, also owned by the IOThread, used for requests
33 that aren't associated with a profile.
34 * Each profile, including incognito profiles, has a number of URLRequestContexts
35 that are created as needed:
36 * The main URLRequestContext is mostly created in ProfileIOData, though it
37 has a couple components that are passed in from content's StoragePartition
38 code. Several other components are shared with the system URLRequestContext,
39 like the HostResolver.
40 * Each non-incognito profile also has a media request context, which uses a
41 different on-disk cache than the main request context. This prevents a
42 single huge media file from evicting everything else in the cache.
43 * On desktop platforms, each profile has a request context for extensions.
44 * Each profile has two contexts for each isolated app (One for media, one
45 for everything else).
46
47 The primary use of the URLRequestContext is to create URLRequest objects using
48 URLRequestContext::CreateRequest(). The URLRequest is the main interface used
49 by consumers of the network stack. It is used to make the actual requests to a
50 server. Each URLRequest tracks a single request across all redirects until an
51 error occurs, it's canceled, or a final response is received, with a (possibly
52 empty) body.
53
54 The HttpNetworkSession is another major network stack object. It owns the
55 HttpStreamFactory, the socket pools, and the HTTP/2 and QUIC session pools. It
56 also has non-owning pointers to the network stack objects that more directly
57 deal with sockets.
58
59 This document does not mention either of these objects much, but at layers
60 above the HttpStreamFactory, objects often grab their dependencies from the
61 URLRequestContext, while the HttpStreamFactory and layers below it generally
62 get their dependencies from the HttpNetworkSession.
63
64
65 # How many "Delegates"?
66
67 The network stack informs the embedder of important events for a request using
68 two main interfaces: the URLRequest::Delegate interface and the NetworkDelegate
69 interface.
70
71 The URLRequest::Delegate interface consists of a small set of callbacks needed
72 to let the embedder drive a request forward. URLRequest::Delegates generally own
73 the URLRequest.
74
75 The NetworkDelegate is an object pointed to by the URLRequestContext and shared
76 by all requests, and includes callbacks corresponding to most of the
77 URLRequest::Delegate's callbacks, as well as an assortment of other methods. The
78 NetworkDelegate is optional, while the URLRequest::Delegate is not.
79
80
81 # Life of a Simple URLRequest
82
83 A request for data is normally dispatched from a child to the browser process.
84 There a URLRequest is created to drive the request. A protocol-specific job
85 (e.g. HTTP, data, file) is attached to the request. That job first checks the
86 cache, and then creates a network connection object, if necessary, to actually
87 fetch the data. That connection object interacts with network socket pools to
88 potentially re-use sockets; the socket pools create and connect a socket if
89 there is no appropriate existing socket. Once that socket exists, the HTTP
90 request is dispatched, the response read and parsed, and the result returned
91 back up the stack and sent over to the child process.
92
93 Of course, it's not quite that simple :-}.
94
95 Consider a simple request issued by a child process. Suppose it's an HTTP
96 request, the response is uncompressed, no matching entry in the cache, and there
97 are no idle sockets connected to the server in the socket pool.
98
99 Continuing with a "simple" URLRequest, here's a bit more detail on how things
100 work.
101
102 ### Request starts in a child process
103
104 Summary:
105
106 * ResourceDispatcher creates an IPCResourceLoaderBridge.
107 * The IPCResourceLoaderBridge asks ResourceDispatcher to start the request.
108 * ResourceDispatcher sends an IPC to the ResourceDispatcherHost in the
109 browser process.
110
111 Chrome has a single browser process, which handles network requests and tab
112 management, among other things, and multiple child processes, which are
113 generally sandboxed so can't send out network requests directly. There are
114 multiple types of child processes (renderer, GPU, plugin, etc). The renderer
115 processes are the ones that layout webpages and run HTML.
116
117 Each child process has at most one ResourceDispatcher, which is responsible for
118 all URL request-related communication with the browser process. When something
119 in another process needs to issue a resource request, it calls into the
120 ResourceDispatcher, which returns an IPCResourceLoaderBridge to the caller.
121 The caller uses the bridge to start a request. When started, the
122 ResourceDispatcher assigns the request a per-renderer ID, and then sends the
123 ID, along with all information needed to issue the request, to the
124 ResourceDispatcherHost in the browser process.
125
126 ### ResourceDispatcherHost sets up the request in the browser process
127
128 Summary:
129
130 * ResourceDispatcherHost uses the URLRequestContext to create the URLRequest.
131 * ResourceDispatcherHost creates a ResourceLoader and a chain of
132 ResourceHandlers to manage the URLRequest.
133 * ResourceLoader starts the URLRequest.
134
135 The ResourceDispatcherHost (RDH), along with most of the network stack, lives
136 on the browser process's IO thread. The browser process only has one RDH,
137 which is responsible for handling all network requests initiated by
138 ResourceDispatchers in all child processes, not just renderer processes.
139 Requests initiated in the browser process don't go through the RDH, with some
140 exceptions.
141
142 When the RDH sees the request, it calls into a URLRequestContext to create the
143 URLRequest. The URLRequestContext has pointers to all the network stack
144 objects needed to issue the request over the network, such as the cache, cookie
145 store, and host resolver. The RDH then creates a chain of ResourceHandlers
146 each of which can monitor/modify/delay/cancel the URLRequest and the
147 information it returns. The only one of these I'll talk about here is the
148 AsyncResourceHandler, which is the last ResourceHandler in the chain. The RDH
149 then creates a ResourceLoader (which is the URLRequest::Delegate), passes
150 ownership of the URLRequest and the ResourceHandler chain to it, and then starts
151 the ResourceLoader.
152
153 The ResourceLoader checks that none of the ResourceHandlers want to cancel,
154 modify, or delay the request, and then finally starts the URLRequest.
155
156 ### Check the cache, request an HttpStream
157
158 Summary:
159
160 * The URLRequest asks the URLRequestJobFactory to create a URLRequestJob, in
161 this case, a URLRequestHttpJob.
162 * The URLRequestHttpJob asks the HttpCache to create an HttpTransaction
163 (always an HttpCache::Transaction).
164 * The HttpCache::Transaction sees there's no cache entry for the request,
165 and creates an HttpNetworkTransaction.
166 * The HttpNetworkTransaction calls into the HttpStreamFactory to request an
167 HttpStream.
168
169 The URLRequest then calls into the URLRequestJobFactory to create a
170 URLRequestJob and then starts it. In the case of an HTTP or HTTPS request, this
171 will be a URLRequestHttpJob. The URLRequestHttpJob attaches cookies to the
172 request, if needed.
173
174 The URLRequestHttpJob calls into the HttpCache to create an
175 HttpCache::Transaction. If there's no matching entry in the cache, the
176 HttpCache::Transaction will just call into the HttpNetworkLayer to create an
177 HttpNetworkTransaction, and transparently wrap it. The HttpNetworkTransaction
178 then calls into the HttpStreamFactory to request an HttpStream to the server.
179
180 ### Create an HttpStream
181
182 Summary:
183
184 * HttpStreamFactory creates an HttpStreamFactoryImpl::Job.
185 * HttpStreamFactoryImpl::Job calls into the TransportClientSocketPool to
186 populate an ClientSocketHandle.
187 * TransportClientSocketPool has no idle sockets, so it creates a
188 TransportConnectJob and starts it.
189 * TransportConnectJob creates a StreamSocket and establishes a connection.
190 * TransportClientSocketPool puts the StreamSocket in the ClientSocketHandle,
191 and calls into HttpStreamFactoryImpl::Job.
192 * HttpStreamFactoryImpl::Job creates an HttpBasicStream, which takes
193 ownership of the ClientSocketHandle.
194 * It returns the HttpBasicStream to the HttpNetworkTransaction.
195
196 The HttpStreamFactoryImpl::Job creates a ClientSocketHandle to hold a socket,
197 once connected, and passes it into the ClientSocketPoolManager. The
198 ClientSocketPoolManager assembles the TransportSocketParams needed to
199 establish the connection and creates a group name ("host:port") used to
200 identify sockets that can be used interchangeably.
201
202 The ClientSocketPoolManager directs the request to the
203 TransportClientSocketPool, since there's no proxy and it's an HTTP request. The
204 request is forwarded to the pool's ClientSocketPoolBase<TransportSocketParams>'s
205 ClientSocketPoolBaseHelper. If there isn't already an idle connection, and there
206 are available socket slots, the ClientSocketPoolBaseHelper will create a new
207 TransportConnectJob using the aforementioned params object. This Job will do the
208 actual DNS lookup by calling into the HostResolverImpl, if needed, and then
209 finally establishes a connection.
210
211 Once the socket is connected, ownership of the socket is passed to the
212 ClientSocketHandle. The HttpStreamFactoryImpl::Job is then informed the
213 connection attempt succeeded, and it then creates an HttpBasicStream, which
214 takes ownership of the ClientSocketHandle. It then passes ownership of the
215 HttpBasicStream back to the HttpNetworkTransaction.
216
217 ### Send request and read the response headers
218
219 Summary:
220
221 * HttpNetworkTransaction gives the request headers to the HttpBasicStream,
222 and tells it to start the request.
223 * HttpBasicStream sends the request, and waits for the response.
224 * The HttpBasicStream sends the response headers back to the
225 HttpNetworkTransaction.
226 * The response headers are sent up to the URLRequest, to the ResourceLoader,
227 and down through the ResourceHandler chain.
228 * They're then sent by the the last ResourceHandler in the chain (the
229 AsyncResourceHandler) to the ResourceDispatcher, with an IPC.
230
231 The HttpNetworkTransaction passes the request headers to the HttpBasicStream,
232 which uses an HttpStreamParser to (finally) format the request headers and body
233 (if present) and send them to the server.
234
235 The HttpStreamParser waits to receive the response and then parses the HTTP/1.x
236 response headers, and then passes them up through both the
237 HttpNetworkTransaction and HttpCache::Transaction to the URLRequestHttpJob. The
238 URLRequestHttpJob saves any cookies, if needed, and then passes the headers up
239 to the URLRequest and on to the ResourceLoader.
240
241 The ResourceLoader passes them through the chain of ResourceHandlers, and then
242 they make their way to the AsyncResourceHandler. The AsyncResourceHandler uses
243 the renderer process ID ("child ID") to figure out which process the request
244 was associated with, and then sends the headers along with the request ID to
245 that process's ResourceDispatcher. The ResourceDispatcher uses the ID to
246 figure out which IPCResourceLoaderBridge the headers should be sent to, which
247 sends them on to whatever created the IPCResourceLoaderBridge in the first
248 place.
249
250 ### Response body is read
251
252 Summary:
253
254 * AsyncResourceHandler allocates a 512k ring buffer of shared memory to read
255 the body of the request.
256 * AsyncResourceHandler tells the ResourceLoader to read the response body to
257 the buffer, 32kB at a time.
258 * AsyncResourceHandler informs the ResourceDispatcher of each read using
259 cross-process IPCs.
260 * ResourceDispatcher tells the AsyncResourceHandler when it's done with the
261 data with each read, so it knows when parts of the buffer can be reused.
262
263 Without waiting to hear back from the ResourceDispatcher, the ResourceLoader
264 tells its ResourceHandler chain to allocate memory to receive the response
265 body. The AsyncResourceHandler creates a 512KB ring buffer of shared memory,
266 and then passes the first 32KB of it to the ResourceLoader for the first read.
267 The ResourceLoader then passes a 32KB body read request down through the
268 URLRequest all the way down to the HttpResponseParser. Once some data is read,
269 possibly less than 32KB, the number of bytes read makes its way back to the
270 AsyncResourceHandler, which passes the shared memory buffer and the offset and
271 amount of data read to the renderer process.
272
273 The AsyncResourceHandler relies on ACKs from the renderer to prevent it from
274 overwriting data that the renderer has yet to consume. This process repeats
275 until the response body is completely read.
276
277 ### URLRequest is destroyed
278
279 Summary:
280
281 * When complete, the RDH deletes the ResourceLoader, which deletes the
282 URLRequest and the ResourceHandler chain.
283 * During destruction, the HttpNetworkTransaction determines if the socket is
284 reusable, and if so, tells the HttpBasicStream to return it to the socket pool.
285
286 When the URLRequest informs the ResourceLoader it's complete, the
287 ResourceLoader tells the ResourceHandlers, and the AsyncResourceHandler tells
288 the ResourceDispatcher the request is complete. The RDH then deletes
289 ResourceLoader, which deletes the URLRequest and ResourceHandler chain.
290
291 When the HttpNetworkTransaction is being torn down, it figures out if the
292 socket is reusable. If not, it tells the HttpBasicStream to close the socket.
293 Either way, the ClientSocketHandle returns the socket is then returned to the
294 socket pool, either for reuse or so the socket pool knows it has another free
295 socket slot.
296
297
298 # Additional Topics
299
300 ## HTTP Cache
301
302 The HttpCache::Transaction sits between the URLRequestHttpJob and the
303 HttpNetworkTransaction, and implements the HttpTransaction interface, just like
304 the HttpNetworkTransaction. The HttpCache::Transaction checks if a request can
305 be served out of the cache. If a request needs to be revalidated, it handles
306 sending a 204 revalidation request over the network. It may also break a range
307 request into multiple cached and non-cached contiguous chunks, and may issue
308 multiple network requests for a single range URLRequest.
309
310 The HttpCache::Transaction uses one of three disk_cache::Backends to actually
311 store the cache's index and files: The in memory backend, the blockfile cache
312 backend, and the simple cache backend. The first is used in incognito. The
313 latter two are both stored on disk, and are used on different platforms.
314
315 One important detail is that it has a read/write lock for each URL. The lock
316 technically allows multiple reads at once, but since an HttpCache::Transaction
317 always grabs the lock for writing and reading before downgrading it to a read
318 only lock, all requests for the same URL are effectively done serially. The
319 renderer process merges requests for the same URL in many cases, which mitigates
320 this problem to some extent.
321
322 It's also worth noting that each renderer process also has its own in-memory
323 cache, which has no relation to the cache implemented in net/, which lives in
324 the browser process.
325
326 ## Cancellation
327
328 A request can be cancelled by the child process, by any of the
329 ResourceHandlers in the chain, or by the ResourceDispatcherHost itself. When the
330 cancellation message reaches the URLRequest, it passes on the fact it's been
331 cancelled back to the ResourceLoader, which then sends the message down the
332 ResourceHandler chain.
333
334 When an HttpNetworkTransaction for a cancelled request is being torn down, it
335 figures out if the socket the HttpStream owns can potentially be reused, based
336 on the protocol (HTTP / HTTP/2 / QUIC) and any received headers. If the socket
337 potentially can be reused, an HttpResponseBodyDrainer is created to try and
338 read any remaining body bytes of the HttpStream, if any, before returning the
339 socket to the SocketPool. If this takes too long, or there's an error, the
340 socket is closed instead. Since this all happens at the layer below the cache,
341 any drained bytes are not written to the cache, and as far as the cache layer is
342 concerned, it only has a partial response.
343
344 ## Redirects
345
346 The URLRequestHttpJob checks if headers indicate a redirect when it receives
347 them from the next layer down (Typically the HttpCache::Transaction). If they
348 indicate a redirect, it tells the cache the response is complete, ignoring the
349 body, so the cache only has the headers. The cache then treats it as a complete
350 entry, even if the headers indicated there will be a body.
351
352 The URLRequestHttpJob then checks with the URLRequest if the redirect should be
353 followed. The URLRequest then informs the ResourceLoader about the redirect, to
354 give it a chance to cancel the request. The information makes its way down
355 through the AsyncResourceHandler into the other process, via the
356 ResourceDispatcher. Whatever issued the original request then checks if the
357 redirect should be followed.
358
359 The ResourceDispatcher then asynchronously sends a message back to either
360 follow the redirect or cancel the request. In either case, the old
361 HttpTransaction is destroyed, and the HttpNetworkTransaction attempts to drain
362 the socket for reuse, just as in the cancellation case. If the redirect is
363 followed, the URLRequest calls into the URLRequestJobFactory to create a new
364 URLRequestJob, and then starts it.
365
366 ## Filters (gzip, SDCH, etc)
367
368 When the URLRequestHttpJob receives headers, it sends a list of all
369 Content-Encoding values to Filter::Factory, which creates a (possibly empty)
370 chain of filters. As body bytes are received, they're passed through the
371 filters at the URLRequestJob layer and the decoded bytes are passed back to the
372 URLRequest::Delegate.
373
374 Since this is done above the cache layer, the cache stores the responses prior
375 to decompression. As a result, if files aren't compressed over the wire, they
376 aren't compressed in the cache, either. This behavior can create problems when
377 responses are SDCH compressed, as a dictionary and a cached file encoded using
378 it may have different lifetimes.
379
380 ## Socket Pools
381
382 The ClientSocketPoolManager is responsible for assembling the parameters needed
383 to connect a socket, and then sending the request to the right socket pool.
384 Each socket request sent to a socket pool comes with a socket params object, a
385 ClientSocketHandle, and a "group name". The params object contains all the
386 information a ConnectJob needs to create a connection of a given type, and
387 different types of socket pools take different params types. The
388 ClientSocketHandle will take temporary ownership of a connected socket and
389 return it to the socket pool when done. All connections with the same group name
390 in the same pool can be used to service the same connection requests, so it
391 consists of host, port, protocol, and whether "privacy mode" is enabled for
392 sockets in the goup.
393
394 All socket pool classes derive from the ClientSocketPoolBase<SocketParamType>.
395 The ClientSocketPoolBase handles managing sockets - which requests to create
396 sockets for, which requests get connected sockets first, which sockets belong
397 to which groups, connection limits per group, keeping track of and closing idle
398 sockets, etc. Each ClientSocketPoolBase subclass has its own ConnectJob type,
399 which establishes a connection using the socket params, before the pool hands
400 out the connected socket.
401
402 ### Socket Pool Layering
403
404 Some socket pools are layered on top other socket pools. This is done when a
405 "socket" in a higher layer needs to establish a connection in a lower level
406 pool and then take ownership of it as part of its connection process. For
407 example, each socket in the SSLClientSocketPool is layered on top of a socket
408 in the TransportClientSocketPool. There are a couple additional complexities
409 here.
410
411 From the perspective of the lower layer pool, all of its sockets that a higher
412 layer pools owns are actively in use, even when the higher layer pool considers
413 them idle. As a result, when a lower layer pool is at its connection limit and
414 needs to make a new connection, it will ask any higher layer pools pools to
415 close an idle connection if they have one, so it can make a new connection.
416
417 Since sockets in the higher layer pool are also in a group in the lower layer
418 pool, they must have their own distinct group name. This is needed so that, for
419 instance, SSL and HTTP connections won't be grouped together in the
420 TcpClientSocketPool, which the SSLClientSocketPool sits on top of.
421
422 ### SSL
423
424 When an SSL connection is needed, the ClientSocketPoolManager assembles the
425 parameters needed both to connect the TCP socket and establish an SSL
426 connection. It then passes them to the SSLClientSocketPool, which creates
427 an SSLConnectJob using them. The SSLConnectJob's first step is to call into the
428 TransportSocketPool to establish a TCP connection.
429
430 Once a connection is established by the lower layered pool, the SSLConnectJob
431 then starts SSL negotiation. Once that's done, the SSL socket is passed back to
432 the HttpStreamFactoryImpl::Job that initiated the request, and things proceed
433 just as with HTTP. When complete, the socket is returned to the
434 SSLClientSocketPool.
435
436 ## Proxies
437
438 Each proxy has its own completely independent set of socket pools. They have
439 their own exclusive TransportSocketPool, their own protocol-specific pool above
440 it, and their own SSLSocketPool above that. HTTPS proxies also have a second
441 SSLSocketPool between the the HttpProxyClientSocketPool and the
442 TransportSocketPool, since they can talk SSL to both the proxy and the
443 destination server, layered on top of each other.
444
445 The first step the HttpStreamFactoryImpl::Job performs, just before calling
446 into the ClientSocketPoolManager to create a socket, is to pass the URL to the
447 Proxy service to get an ordered list of proxies (if any) that should be tried
448 for that URL. Then when the ClientSocketPoolManager tries to get a socket for
449 the Job, it uses that list of proxies to direct the request to the right socket
450 pool.
451
452 ## Alternate Protocols
453
454 ### HTTP/2 (Formerly SPDY)
455
456 HTTP/2 negotation is performed as part of the SSL handshake, so when
457 HttpStreamFactoryImpl::Job gets a socket, it may have HTTP/2 negotiated over it
458 as well. When it gets a socket with HTTP/2 negotiated as well, the Job creates a
459 SpdySession using the socket and a SpdyHttpStream on top of the SpdySession.
460 The SpdyHttpStream will be passed to the HttpNetworkTransaction, which drives
461 the stream as usual.
462
463 The SpdySession will be shared with other Jobs connecting to the same server,
464 and future Jobs will find the SpdySession before they try to create a
465 connection. HttpServerProperties also tracks which servers supported HTTP/2 when
466 we last talked to them. We only try to establish a single connection to servers
467 we think speak HTTP/2 when multiple HttpStreamFactoryImpl::Jobs are trying to
468 connect to them, to avoid wasting resources.
469
470 ### QUIC
471
472 QUIC works quite a bit differently from HTTP/2. Servers advertise QUIC support
473 with an "Alternate-Protocol" HTTP header in their responses.
474 HttpServerProperties then tracks servers that have advertised QUIC support.
475
476 When a new request comes in to HttpStreamFactoryImpl for a connection to a
477 server that has advertised QUIC support in the past, it will create a second
478 HttpStreamFactoryImpl::Job for QUIC, which returns an QuicHttpStream on success.
479 The two Jobs (One for QUIC, one for all versions of HTTP) will be raced against
480 each other, and whichever successfully creates an HttpStream first will be used.
481
482 As with HTTP/2, once a QUIC connection is established, it will be shared with
483 other Jobs connecting to the same server, and future Jobs will just reuse the
484 existing QUIC session.
485
486 ## Prioritization
487
488 URLRequests are assigned a priority on creation. It only comes into play in
489 a couple places:
490
491 * The ResourceScheduler lives outside net/, and in some cases, delays starting
492 low priority requests on a per-tab basis.
493 * DNS lookups are initiated based on the highest priority request for a lookup.
494 * Socket pools hand out and create sockets based on prioritization. However,
495 when a socket becomes idle, it will be assigned to the highest priority request
496 for the server its connected to, even if there's a higher priority request to
497 another server that's waiting on a free socket slot.
498 * HTTP/2 and QUIC both support sending priorities over-the-wire.
499
500 At the socket pool layer, sockets are only assigned to socket requests once the
501 socket is connected and SSL is negotiated, if needed. This is done so that if
502 a higher priority request for a group reaches the socket pool before a
503 connection is established, the first usable connection goes to the highest
504 priority socket request.
505
506 ## Non-HTTP Schemes
507
508 The URLRequestJobFactory has a ProtocolHander for each supported scheme.
509 Non-HTTP URLRequests have their own ProtocolHandlers. Some are implemented in
510 net/, (like FTP, file, and data, though the renderer handles some data URLs
511 internally), and others are implemented in content/ or chrome (like blob,
512 chrome, and chrome-extension).
OLDNEW
« no previous file with comments | « no previous file | net/net.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698