| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_WRITER_H_ |
| #define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_WRITER_H_ |
| |
| #include <stddef.h> |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| |
| #include "base/callback.h" |
| #include "base/memory/weak_ptr.h" |
| #include "content/common/content_export.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/net_errors.h" |
| |
| namespace content { |
| |
| struct HttpResponseInfoIOBuffer; |
| class ServiceWorkerResponseReader; |
| class ServiceWorkerResponseWriter; |
| |
| // This class is responsible for possibly updating the ServiceWorker script |
| // cache for an installed ServiceWorker main script. If there is no existing |
| // cache entry, this class always writes supplied data back to the cache; if |
| // there is an existing cache entry, this class only writes supplied data back |
| // if there is a cache mismatch. |
| // |
| // Note that writes done by this class cannot be "short" - ie, if they succeed, |
| // they always write all the supplied data back. Therefore completions are |
| // signalled with net::Error without a count of bytes written. |
| // |
| // This class's behavior is modelled as a state machine; see the DoLoop function |
| // for comments about this. |
| class CONTENT_EXPORT ServiceWorkerCacheWriter { |
| public: |
| using OnWriteCompleteCallback = base::OnceCallback<void(net::Error)>; |
| |
| // The |compare_reader| may be null, in which case this instance will |
| // unconditionally write back data supplied to |MaybeWriteHeaders| and |
| // |MaybeWriteData|. |
| // |
| // When |pause_when_not_identical| is true and the cache writer detects a |
| // difference between bodies from the network and from the storage, the |
| // comparison stops immediately and the cache writer returns |
| // net::ERR_IO_PENDING, with nothing written to the storage. |
| ServiceWorkerCacheWriter( |
| std::unique_ptr<ServiceWorkerResponseReader> compare_reader, |
| std::unique_ptr<ServiceWorkerResponseReader> copy_reader, |
| std::unique_ptr<ServiceWorkerResponseWriter> writer, |
| bool pause_when_not_identical); |
| |
| ~ServiceWorkerCacheWriter(); |
| |
| // Writes the supplied |headers| back to the cache. Returns ERR_IO_PENDING if |
| // the write will complete asynchronously, in which case |callback| will be |
| // called when it completes. Otherwise, returns a code other than |
| // ERR_IO_PENDING and does not invoke |callback|. Note that this method will |
| // not necessarily write data back to the cache if the incoming data is |
| // equivalent to the existing cached data. See the source of this function for |
| // details about how this function drives the state machine. |
| net::Error MaybeWriteHeaders(HttpResponseInfoIOBuffer* headers, |
| OnWriteCompleteCallback callback); |
| |
| // Writes the supplied body data |data| back to the cache. Returns |
| // ERR_IO_PENDING if the write will complete asynchronously, in which case |
| // |callback| will be called when it completes. Otherwise, returns a code |
| // other than ERR_IO_PENDING and does not invoke |callback|. Note that this |
| // method will not necessarily write data back to the cache if the incoming |
| // data is equivalent to the existing cached data. See the source of this |
| // function for details about how this function drives the state machine. |
| net::Error MaybeWriteData(net::IOBuffer* buf, |
| size_t buf_size, |
| OnWriteCompleteCallback callback); |
| |
| // Returns a count of bytes written back to the cache. |
| size_t bytes_written() const { return bytes_written_; } |
| bool did_replace() const { return did_replace_; } |
| bool is_pausing() const { return state_ == STATE_PAUSING; } |
| |
| // Resumes a cache writer which were paused when a block of data from the |
| // network wasn't identical to the data in the storage. This method is valid |
| // only when |pause_when_not_identical| is true in the constructor and |
| // |state_| is STATE_PAUSING. |
| net::Error Resume(OnWriteCompleteCallback callback); |
| |
| private: |
| // States for the state machine. |
| // |
| // The state machine flows roughly like this: if there is no existing cache |
| // entry, incoming headers and data are written directly back to the cache |
| // ("passthrough mode", the PASSTHROUGH states). If there is an existing cache |
| // entry, incoming headers and data are compared to the existing cache entry |
| // ("compare mode", the COMPARE states); if at any point the incoming |
| // headers/data are not equal to the cached headers/data, this class copies |
| // the cached data up to the point where the incoming data and the cached data |
| // diverged ("copy mode", the COPY states), then switches to "passthrough |
| // mode" to write the remainder of the incoming data. The overall effect is to |
| // avoid rewriting the cache entry if the incoming data is identical to the |
| // cached data. |
| // |
| // Note that after a call to MaybeWriteHeaders or MaybeWriteData completes, |
| // the machine is always in STATE_DONE, indicating that the call is finished; |
| // those methods are responsible for setting a new initial state. |
| enum State { |
| STATE_START, |
| // Control flows linearly through these four states, then loops from |
| // READ_DATA_FOR_COMPARE_DONE to READ_DATA_FOR_COMPARE, or exits to |
| // READ_HEADERS_FOR_COPY. |
| STATE_READ_HEADERS_FOR_COMPARE, |
| STATE_READ_HEADERS_FOR_COMPARE_DONE, |
| STATE_READ_DATA_FOR_COMPARE, |
| STATE_READ_DATA_FOR_COMPARE_DONE, |
| |
| // The cache writer is paused because the network data wasn't identical with |
| // the stored data, and |pause_when_not_identical| is true. |
| STATE_PAUSING, |
| |
| // Control flows linearly through these states, with each pass from |
| // READ_DATA_FOR_COPY to WRITE_DATA_FOR_COPY_DONE copying one block of data |
| // at a time. Control loops from WRITE_DATA_FOR_COPY_DONE back to |
| // READ_DATA_FOR_COPY if there is more data to copy, or exits to |
| // WRITE_DATA_FOR_PASSTHROUGH. |
| STATE_READ_HEADERS_FOR_COPY, |
| STATE_READ_HEADERS_FOR_COPY_DONE, |
| STATE_WRITE_HEADERS_FOR_COPY, |
| STATE_WRITE_HEADERS_FOR_COPY_DONE, |
| STATE_READ_DATA_FOR_COPY, |
| STATE_READ_DATA_FOR_COPY_DONE, |
| STATE_WRITE_DATA_FOR_COPY, |
| STATE_WRITE_DATA_FOR_COPY_DONE, |
| |
| // Control flows linearly through these states, with a loop between |
| // WRITE_DATA_FOR_PASSTHROUGH and WRITE_DATA_FOR_PASSTHROUGH_DONE. |
| STATE_WRITE_HEADERS_FOR_PASSTHROUGH, |
| STATE_WRITE_HEADERS_FOR_PASSTHROUGH_DONE, |
| STATE_WRITE_DATA_FOR_PASSTHROUGH, |
| STATE_WRITE_DATA_FOR_PASSTHROUGH_DONE, |
| |
| // This state means "done with the current call; ready for another one." |
| STATE_DONE, |
| }; |
| |
| // Drives this class's state machine. This function steps the state machine |
| // until one of: |
| // a) One of the state functions returns an error |
| // b) The state machine reaches STATE_DONE |
| // A successful value (net::OK or greater) indicates that the requested |
| // operation completed synchronously. A return value of ERR_IO_PENDING |
| // indicates that some step had to submit asynchronous IO for later |
| // completion, and the state machine will resume running (via AsyncDoLoop) |
| // when that asynchronous IO completes. Any other return value indicates that |
| // the requested operation failed synchronously. |
| int DoLoop(int result); |
| |
| // State handlers. See function comments in the corresponding source file for |
| // details on these. |
| int DoStart(int result); |
| int DoReadHeadersForCompare(int result); |
| int DoReadHeadersForCompareDone(int result); |
| int DoReadDataForCompare(int result); |
| int DoReadDataForCompareDone(int result); |
| int DoReadHeadersForCopy(int result); |
| int DoReadHeadersForCopyDone(int result); |
| int DoWriteHeadersForCopy(int result); |
| int DoWriteHeadersForCopyDone(int result); |
| int DoReadDataForCopy(int result); |
| int DoReadDataForCopyDone(int result); |
| int DoWriteDataForCopy(int result); |
| int DoWriteDataForCopyDone(int result); |
| int DoWriteHeadersForPassthrough(int result); |
| int DoWriteHeadersForPassthroughDone(int result); |
| int DoWriteDataForPassthrough(int result); |
| int DoWriteDataForPassthroughDone(int result); |
| int DoDone(int result); |
| |
| // Wrappers for asynchronous calls. These are responsible for scheduling a |
| // callback to drive the state machine if needed. These either: |
| // a) Return ERR_IO_PENDING, and schedule a callback to run the state |
| // machine's Run() later, or |
| // b) Return some other value and do not schedule a callback. |
| int ReadInfoHelper(const std::unique_ptr<ServiceWorkerResponseReader>& reader, |
| HttpResponseInfoIOBuffer* buf); |
| int ReadDataHelper(const std::unique_ptr<ServiceWorkerResponseReader>& reader, |
| net::IOBuffer* buf, |
| int buf_len); |
| int WriteInfoHelper( |
| const std::unique_ptr<ServiceWorkerResponseWriter>& writer, |
| HttpResponseInfoIOBuffer* buf); |
| int WriteDataHelper( |
| const std::unique_ptr<ServiceWorkerResponseWriter>& writer, |
| net::IOBuffer* buf, |
| int buf_len); |
| |
| // Callback used by the above helpers for their IO operations. This is only |
| // run when those IO operations complete asynchronously, in which case it |
| // invokes the synchronous DoLoop function and runs the client callback (the |
| // one passed into MaybeWriteData/MaybeWriteHeaders) if that invocation |
| // of DoLoop completes synchronously. |
| void AsyncDoLoop(int result); |
| |
| State state_; |
| // Note that this variable is only used for assertions; it reflects "state != |
| // DONE && not in synchronous DoLoop". |
| bool io_pending_; |
| bool comparing_; |
| |
| scoped_refptr<HttpResponseInfoIOBuffer> headers_to_read_; |
| scoped_refptr<HttpResponseInfoIOBuffer> headers_to_write_; |
| scoped_refptr<net::IOBuffer> data_to_read_; |
| int len_to_read_; |
| scoped_refptr<net::IOBuffer> data_to_copy_; |
| scoped_refptr<net::IOBuffer> data_to_write_; |
| int len_to_write_; |
| OnWriteCompleteCallback pending_callback_; |
| |
| size_t cached_length_; |
| |
| // The amount of data from the network (|data_to_write_|) which has already |
| // been compared with data from storage (|data_to_read_|). This is |
| // initialized to 0 for every new arrival of network data. |
| size_t compare_offset_; |
| |
| // Count of bytes which has been read from the network for comparison, and |
| // known as identical with the stored scripts. It is incremented only when a |
| // full block of network data is compared, to avoid having to use only |
| // fragments of the buffered network data. |
| size_t bytes_compared_; |
| |
| // Count of bytes copied from |copy_reader_| to |writer_|. |
| size_t bytes_copied_; |
| |
| // Count of bytes written back to |writer_|. |
| size_t bytes_written_; |
| |
| bool did_replace_ = false; |
| |
| // When the cache writer finds any differences between bodies from the network |
| // and from the storage, and the |pause_when_not_identical_| is true, the |
| // cache writer pauses immediately. |
| const bool pause_when_not_identical_; |
| |
| std::unique_ptr<ServiceWorkerResponseReader> compare_reader_; |
| std::unique_ptr<ServiceWorkerResponseReader> copy_reader_; |
| std::unique_ptr<ServiceWorkerResponseWriter> writer_; |
| base::WeakPtrFactory<ServiceWorkerCacheWriter> weak_factory_; |
| }; |
| |
| } // namespace content |
| |
| #endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_WRITER_H_ |