weexteam/weex_devtools_android

View on GitHub
protocol/src/main/java/com/taobao/weex/devtools/inspector/network/NetworkEventReporter.java

Summary

Maintainability
A
35 mins
Test Coverage
/*
 * Copyright (c) 2014-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

package com.taobao.weex.devtools.inspector.network;

import android.support.annotation.Nullable;

import java.io.IOException;
import java.io.InputStream;

/**
 * Interface that callers must invoke in order to supply data to the Network tab in
 * the WebKit Inspector.  For HTTP specific traffic, the following call flow must be met:
 *
 * <pre>
 * requestWillBeSent +--- responseHeadersReceived +--- interpretResponseStream
 *                   |           |                 |
 *                   |           `--- dataSent    |
 *                   |                             |
 *                   `-----------------------------`-------- httpExchangeFailed
 * </pre>
 *
 * <p>Note that {@link #interpretResponseStream} combined with {@link DefaultResponseHandler}
 * will automatically invoke {@link #dataReceived}, {@link #responseReadFailed} and
 * {@link #responseReadFinished}.  If you use your own custom {@link ResponseHandler} you
 * must be sure to invoke these methods manually.</p>
 *
 * <p>For arbitrary sockets or explicitly for WebSockets, the following call flow must be met:</p>
 *
 * <pre>
 * webSocketCreated +--- webSocketWillSendHandshakeRequest ---- webSocketHandshakeResponseReceived
 *                  |                                                              |
 *                  |                      ----------------------+-----------------+--------
 *                  |                     |                      |                          |
 *                  +--------- [ webSocketFrameSent | webSocketFrameReceiver ] ---          |
 *                  |                                           ^                |          |
 *                  |                     `----------------------+----------------+         |
 *                  |                                                             |         |
 *                  `--------- webSocketClosed ----------------------------------           |
 *                                                                                          |
 *                                     `----------------------------------------------------
 * </pre>
 *
 * <p>Explicitly worth nothing is that regular sockets in an Android app can be treated as
 * WebSockets for the purpose of arbitrary socket inspection and can skip
 * {@link #webSocketWillSendHandshakeRequest} and {@link #webSocketHandshakeResponseReceived}
 * which are only used for the WebSocket-specific HTTP upgrade.</p>
 */
public interface NetworkEventReporter {
    /**
     * Returns true if there is at least one peer listening for network events; false otherwise.
     * This value is provided as an optimization to avoid expensive work when the WebKit Inspector is
     * not being used.  It is otherwise safe to invoke methods defined in this interface when
     * the value is false.
     */
    boolean isEnabled();

    /**
     * Indicates that a request is about to be sent, but has not yet been delivered over the wire.
     *
     * @param request Request descriptor.
     */
    void requestWillBeSent(InspectorRequest request);

    /**
     * Indicates that a response message was just received from the network, but the body
     * has not yet been read.
     *
     * @param response Response descriptor.
     */
    void responseHeadersReceived(InspectorResponse response);

    /**
     * Indicates that communication with the server has failed. You are expected to call this for any
     * exception before you call {@link #interpretResponseStream}. After
     * {@link #interpretResponseStream} is called we will reporting any
     * {@link IOException} during reading from the {@link InputStream}.
     *
     * @param requestId Unique identifier for the request as per {@link InspectorRequest#id()}
     * @param errorText Text to report for the error; using {@link IOException#toString()} is
     *     recommended.
     */
    void httpExchangeFailed(String requestId, String errorText);

    /**
     * Intercept the stream as given by the underlying HTTP library that contains the body of the
     * response. In order to have the response show up in inspector (and to have the request be
     * completed successfully) you need to call this AND read until exhaustion/EOF of the returned
     * stream.
     *
     * <p>
     * We will internally signal a failure if there is an {@link IOException} received while reading
     * from the stream.
     *
     * <p>
     * Do not invoke {@link #httpExchangeFailed(String, String)} after calling this method.
     *
     * @param requestId Unique identifier for the request as per {@link InspectorRequest#id()}
     * @param contentType The {@code Content-Type} header value that was specified in
     *     {@link InspectorResponse}.  This header is used to determine the appropriate
     *     storage format for the body.  For instance, {@code image/*} is necessary to cause
     *     images to appear in the Inspector UI.
     * @param contentEncoding The {@code Content-Encoding} header value that was specified in
     *     {@link InspectorResponse}.  This header is used to determine what type of decompression
     *     is to be applied when delivering the raw response stream to the debugging interface.
     *     If null, no decompression will be used.
     * @param inputStream Response stream if applicable ("HEAD" for instance does not have a body).
     *     {@code null} otherwise.
     * @param responseHandler Callback to forward stream events back to the relevant event reporter
     *     methods.  Recommend using {@link DefaultResponseHandler} for most callers.
     *
     * @return {@link InputStream} that has been intercepted if WebkitInspector is active and enabled
     *     otherwise it will return {@code inputStream}
     */
    @Nullable
    InputStream interpretResponseStream(
            String requestId,
            @Nullable String contentType,
            @Nullable String contentEncoding,
            @Nullable InputStream inputStream,
            ResponseHandler responseHandler);

    /**
     * Indicates that there was a failure while reading from response stream.  If you use
     * {@link #interpretResponseStream} with {@link DefaultResponseHandler} (as is recommended),
     * this method will be invoked automatically for you.
     *
     * @param requestId Unique identifier for the request as per {@link InspectorRequest#id()}
     * @param errorText Text to report for the error; using {@link IOException#toString()} is
     *     recommended.
     */
    void responseReadFailed(String requestId, String errorText);

    /**
     * Indicates that the response stream has been fully exhausted and the request is now
     * complete.  If you use {@link #interpretResponseStream} with {@link DefaultResponseHandler}
     * (as is recommended), this method will be invoked automatically for you.
     *
     * @param requestId Unique identifier for the request as per {@link InspectorRequest#id()}
     */
    void responseReadFinished(String requestId);

    /**
     * Indicates that raw data was sent over the network.  It is permissible to invoke this
     * method just once after the full size of the request is known.
     * <p>
     * Invoking this method is optional and merely provides additional timing metrics and actual
     * payload sizes to the Inspector UI.
     *
     * @param requestId Unique identifier for the request as per {@link InspectorRequest#id()}
     * @param dataLength Uncompressed data segment length
     * @param encodedDataLength Compressed data segment length
     */
    void dataSent(String requestId, int dataLength, int encodedDataLength);

    /**
     * Indicates that raw data was received from the network.
     *
     * @see #dataSent
     */
    void dataReceived(String requestId, int dataLength, int encodedDataLength);

    /**
     * Provides unique request id for {@link InspectorRequest#id()}.
     */
    String nextRequestId();

    /**
     * Invoked when a socket is created and implicitly being connected (but not necessarily connected
     * yet).  If a websocket is being used, proceed to {@link #webSocketWillSendHandshakeRequest}.
     * Otherwise you may proceed next to {@code webSocketFrame*} methods.
     */
    void webSocketCreated(String requestId, String url);

    /**
     * Socket has been closed for unknown reasons.  Consider first invoking
     * {@link #webSocketFrameError} even for standard sockets to provide context.
     */
    void webSocketClosed(String requestId);

    /**
     * Invoked specifically for websockets to communicate the WebSocket upgrade messages.  Not
     * necessary to call for standard sockets.
     */
    void webSocketWillSendHandshakeRequest(InspectorWebSocketRequest request);

    /**
     * Delivers the reply from the peer in response to the WebSocket upgrade request.
     */
    void webSocketHandshakeResponseReceived(InspectorWebSocketResponse response);

    /**
     * Send a "websocket" frame from our app to the remote peer.  Standard sockets can simply emulate
     * this by capturing each socket send as a frame of either
     * {@link InspectorWebSocketFrame#OPCODE_BINARY} or
     * {@link InspectorWebSocketFrame#OPCODE_TEXT}.  Note that binary
     * payloads are not visualized in Chrome but can be sent by simply assuming the data is UTF-8
     * encoded (yes, really:
     * https://chromium.googlesource.com/chromium/blink/+/master/Source/core/inspector/InspectorResourceAgent.cpp#850).
     */
    void webSocketFrameSent(InspectorWebSocketFrame frame);

    /**
     * The receive side of {@link #webSocketFrameSent}.
     */
    void webSocketFrameReceived(InspectorWebSocketFrame frame);

    /**
     * Indicates a web socket (or standard socket) error has occurred though this doesn't
     * explicitly close the socket (see {@link #webSocketClosed}) but it does let the UI
     * know that it should denote the closure as forceful or as a failure in some way.
     */
    void webSocketFrameError(String requestId, String errorMessage);

    /**
     * Represents the request that will be sent over HTTP.  Note that for many implementations
     * of HTTP the request constructed may differ from the request actually sent over the wire.
     * For instance, additional headers like {@code Host}, {@code User-Agent}, {@code Content-Type},
     * etc may not be part of this request but should be injected if necessary.  Some stacks offer
     * inspection of the raw request about to be sent to the server which is preferable.
     */
    interface InspectorRequest extends InspectorRequestCommon {
        /**
         * Provide an extra integer to decorate the {@link #friendlyName()}.  This shows up next to
         * it in the WebKit Inspector UI and can be used to indicate things like request priority.
         */
        @Nullable
        Integer friendlyNameExtra();

        String url();

        /**
         * HTTP method ("GET", "POST", "DELETE", etc).
         */
        String method();

        /**
         * Provide the body if part of an entity-enclosing request (like "POST" or "PUT").  May
         * return null otherwise.
         */
        @Nullable
        byte[] body() throws IOException;
    }

    interface InspectorResponse extends InspectorResponseCommon {
        String url();

        /**
         * True if the response was furnished on a re-used socket; false otherwise or if unknown.
         */
        boolean connectionReused();

        /**
         * Unique connection identifier representing the socket that was used to furnish the response.
         */
        int connectionId();

        /**
         * True if the response was furnished by disk cache; false otherwise or if unknown.
         */
        boolean fromDiskCache();
    }

    interface InspectorWebSocketRequest extends InspectorRequestCommon {
    }

    interface InspectorWebSocketResponse extends InspectorResponseCommon {
        /**
         * Optional and redundant set of headers from {@link InspectorWebSocketRequest} that are for
         * some mysterious reason are included here in the response by Chrome.
         */
        @Nullable
        InspectorHeaders requestHeaders();
    }

    interface InspectorWebSocketFrame {
        String requestId();

        int OPCODE_CONTINUATION = 0;
        int OPCODE_TEXT = 1;
        int OPCODE_BINARY = 2;
        int OPCODE_CONNECTION_CLOSE = 8;
        int OPCODE_PING = 9;
        int OPCODE_PONG = 10;

        int opcode();
        boolean mask();
        String payloadData();
    }

    interface InspectorRequestCommon extends InspectorHeaders {
        /**
         * Unique identifier for this request.  This identifier must be used in all other network
         * events corresponding to this request.  Identifiers may be re-used for HTTP requests  or
         * WebSockets that have exhuasted the state machine to its final closed/finished state.
         */
        String id();

        /**
         * Arbitrary debug-friendly name of the request.
         */
        String friendlyName();
    }

    interface InspectorResponseCommon extends InspectorHeaders {
        /** @see InspectorRequest#id() */
        String requestId();

        int statusCode();

        String reasonPhrase();
    }

    interface InspectorHeaders {
        int headerCount();
        String headerName(int index);
        String headerValue(int index);

        @Nullable
        String firstHeaderValue(String name);
    }

    interface TimingInspectorResponse extends InspectorResponse{
        @Nullable Timing resourceTiming();
    }
}