I recently wrote MockNet, a lightweight server framework that allows you to quickly build servers on Android or Java platforms to develop and test network interfaces without requiring server knowledge.

The reason for writing this framework

This is what happens when you write a project

  1. Before the server interface is written, the Android client can only do some interface development work first. When it comes to displaying data, it can only save it or fill it with some fake data. After the server interface is written, the code has to be deleted or changed again.
  2. When debugging the network interface, it often needs the background client to cooperate with it. Sometimes it takes a lot of time to confirm whether it is the server or the client, and then it takes a lot of time to debug the network interface.
  3. When there is no Internet but you need to show the functionality of the application, you can only add fake data or data from the database with a bunch of if else. The code is inconvenient to write.

The framework was written to address these issues.

  • First of all, there is the problem of network interface development. Through MockNet library, network interface development can be carried out without the completion of background interface, and the development process should be smoother.
  • The second problem is network interface debugging. Through MockNet library, you can also debug the Android client network interface without the help of background students.
  • Finally, there is the problem that there is no network to demonstrate functionality. With MockNet, it is possible to demonstrate networking functionality without a network.

The framework is introduced

MockNet was written to facilitate the development and testing of a client-side network interface, which simply starts a server locally to respond to a client’s network request. But no knowledge of server development is required to add this functionality in a few lines of code. The code is available on Github at github.com/5A59/MockNe…

Library download

Gradle

// Add the following code to the module's gradle file
compile 'com. Zy. Mocknet: mocknet: 1.0'Copy the code

Maven

<dependency>
  <groupId>com.zy.mocknet</groupId>
  <artifactId>mocknet</artifactId>
  <version>1.0</version>
  <type>pom</type>
</dependency>Copy the code

Jar package download mocknet_1_0_0.jar

Framework using

MockNet is simple to use, with the following steps:

  1. Change the network access IP address to the local IP address (127.0.0.1:port or local real IP address :port) because the server is set up locally.
  2. Initialization.
    / / create MockNet
    MockNet mockNet = MockNet.create();Copy the code
  3. Add processing to the request

    In MockNet, there is a response for each request, and the combination of request and Response is called a MockConnection. To add processing of requests is to add a MockConnection instance.
    MockConnection conn = MockConnectionFactory.getInstance()
     .createGeneralConnection("/ *"."general connection");
    mockNet.addConnection(conn);Copy the code

    CreateGeneralConnection (String, String) The first argument is the request URL, for example, /test, and the second argument is the return content. This is the simplest way to create a MockConnection, as you can see for more examplesMockNet introductionorJava document

  4. Start the service
    // Port 8088 is used by default
    mockNet.start();
    // Use the specified port
    mockNet.start(int port);Copy the code
  5. Close the service
    mockNet.stop();Copy the code

That’s how MockNet is used. You can also write it as a chain call, which makes the code much cleaner.

MockNet mockNet = MockNet.create()
                .addConnection(MockConnectionFactory.getInstance()
                        .createGeneralConnection("/test"."{'res':'ok'}"))
                .addConnection(MockConnectionFactory.getInstance()
                        .createGeneralConnection("/ *"."{'res':'ok'}"))
                .start();Copy the code

Use the advanced

Above is a simple use of MockNet, with many more extended uses.

  1. Custom MockConnection builds the Builder through mockConnection.Builder and builds MockConnection through builder-related methods. Common methods of Builder can be seen in the documentation :Builder

  2. MockNet internally distinguishes requests by URL and Method (GET, POST, etc.). If multiple MockConnections with the same URL and method are added in addConnection, One of these is selected and returned via IConnectionSelector, which is returned randomly using RandomSelector by default. The return rule can be set by implementing the IConnectionSelector interface and by mocknet.setSelector ().

  3. The Log setting outputs logs for each MockConnection by default to aid debugging, and if you want to turn Log off, you can set isLog(false) when MockConnection is built. MockNet Log output is done by Logger and Printer. AndroidPrinter and JavaPrinter are set by default. If you want to customize Log, you can implement Printer interface. The logger.init (yourPrinter) setting is called, but only after mockConnection.create () is called, otherwise the setting is overridden as the default

  4. Request and Response processing in MockNet follows the chain of responsibility pattern (see the framework below) by adding handlers to add processing steps. The framework provides BlockHandler, LogHandler, VerifyHeaderHandler, VerifyParamHandler, and ConnectionHandler. If you want to add your own Handler, implement the Handler interface and set it up via mocknet.addHandler (Handler h). Specific implementation method can refer to the framework of the default implementation of the Handler code.

  5. HTTPS HTTPS can be enabled by using the following code:

    MockNet mockNet = MockNet.create();
    mockNet.start(ServerSocketFactory.createHttpsServerSocket(int port, String jksPath, String storePwd));Copy the code
  6. Dynamic data processing To simplify usage and speed up development, only static data is returned by default. If you want to process request data dynamically, you can inherit the RequestExecutor interface and implement the Execute method. The request is processed dynamically in the execute method and the Reponse is created to return. The Server object is then constructed through the Server constructor and passed in a class object that implements the RequestExecutor interface. See the MockRequestExecutor implementation and the Server constructor for details.

See the MockNet and Java documentation for more usage

The framework architecture

The above introduced the use of the framework, here is the implementation of the framework to do some introduction. Start with an architecture diagram.





mocknet.png

I think the whole architecture is fine. The whole can be divided into two layers, Server layer and Application layer.

Server layer

The main work of the Server layer is socket communication and parsing of Request and Response. The Server class primarily listens for ports, accepts socket requests and creates RequestRunnable requests. RequestRunnable is added to the thread pool once it is created. The RequestRunnable class parses the request and sends it to the RequestExecutor for processing, which returns a Response, RequestRunnable is then responsible for writing the Response to the socket.

The Application layer

The Application layer handles requests. You implement the RequestExecutor interface to process a Request and return a Response. By default, the Framework implements the RequestRunnable interface with the MockRequestExecutor class, which returns static messages. If you want to process requests dynamically, you need to implement the RequestExecutor interface yourself. In MockRequestExecutor, the chain of responsibility model is used to process requests, with each Handler acting as a processing link. The implementation of this chain was learned from looking at the OkHttp code earlier. Here’s the code for a look.

HandlerChain chain = new RealHandlerChain();

public Response execute(Request request) {
    for (Handler h : userHandlers) {
        chain.addHandler(h);
    }

    if (initHandler) {
        chain.addHandler(new BlockHandler());
        chain.addHandler(new VerifyParamHandler());
        chain.addHandler(new VerifyHeaderHandler());
        chain.addHandler(new LogHandler());
        chain.addHandler(new ConnectionHandler());
    }

    return chain.start(request);
}Copy the code

Above is an implementation of the MockRequestExecutor’s execute() function, which first adds the Handler to the chain and then calls chain-.start () to initiate the call to the Handler for processing. Look again at the start() method implementation for HandlerChain.

public Response start(Request request) {
    Handler handler = handlers.get(index);
    Response response = handler.handle(request, this, index);
    return response;
}Copy the code

Handler.handle () is called directly, and the next handler needs to be called in handler’s handle method. The framework implements several handlers by default: BlockHandler, VerifyParamHandler, VeriffyHeaderHandler, LogHandler, and ConnectionHandler. All of these functions are identified by their names, so I’m going to focus on ConnectionHandler.

ConnectionHandler is the endpoint of the call to the processing chain and is responsible for generating Response. The handle method of ConnectionHandler is as follows:

String method = request.getMethod();
String url = request.getRequestUri();
MockConnection connection =
    ConnectionStore.getInstance().getConnection(method, url);
if (connection == null) {
    return Response.create404Response();
}
Response response = new Response();
// omit the setting of Response
return response;Copy the code

MockConnection is retrieved from the ConnectionStore with the request URL and Method, and the Response is created and populated with the Response information in MockConnection. So when did you put MockConnection in ConnectionStore? This was added when we started with mocknet.addConnection ().

The framework is almost complete. For details, see the code. It feels like the whole framework is not difficult to implement, modularity and extensibility are good (brag about it).

other

If you have any questions or ideas, you can make an issue on Github, or send an email to [email protected].