Abstract: “IP direct connection scheme” is mainly to solve DNS pollution, save DNS resolution time, usually we can use NSURLProtocol in the project to intercept NSURLSession request, the following will support a challenge in Post request, and the coping strategy is introduced.

The “DIRECT IP connection solution” mainly solves DNS pollution and saves DNS resolution time. Normally, we can use NSURLProtocol in projects to intercept NSURLSession requests. The following will introduce a challenge in Post requests and countermeasures:

There are several solutions to the problem of missing the body when supporting POST requests:

The scheme is as follows:

1. Use NSURLConnection instead. 2. Get the body using HTTPBodyStream and assign it to body 4. Use Get requests instead of Post requests.

Do the following analysis of the scheme

  • In use NSURLConnection. Compared with NSURLSession, NSURLConnection will encounter more performance problems, and some of Apple’s new features will not be used, so it will eventually be eliminated without consideration.
  • If the value is less than 2M, the Request will be delayed. If the value is more than 10M, Request timeout will be requested directly. And you can’t solve the binary Body problem, because the Header is text.
  • Use Get requests instead of Post requests. This is also possible, but there are limits to how requests can be made, and Post requests still need to be addressed. If it’s based on an old project, it’s too intrusive. This scheme is suitable for new projects.
  • The other method, which we will focus on below, is to use HTTPBodyStream to get the body and assign it to the body.
/ / / / NSURLRequest + CYLNSURLProtocolExtension. H / / / / / / Created by ElonChan on 28/07/2017. / / Copyright © 2017 ChenYilong. All rights reserved. //#import <Foundation/Foundation.h>@interface NSURLRequest (CYLNSURLProtocolExtension) - (NSURLRequest *)cyl_getPostRequestIncludeBody; @ the end / / / / NSURLRequest + CYLNSURLProtocolExtension. H / / / / / / Created by ElonChan on 28/07/2017. / / Copyright © 2017 ChenYilong. All rights reserved. //#import "NSURLRequest+CYLNSURLProtocolExtension.h"

@implementation NSURLRequest (CYLNSURLProtocolExtension)

- (NSURLRequest *)cyl_getPostRequestIncludeBody {
    return [[self cyl_getMutablePostRequestIncludeBody] copy];
}

- (NSMutableURLRequest *)cyl_getMutablePostRequestIncludeBody {
    NSMutableURLRequest * req = [self mutableCopy];
    if ([self.HTTPMethod isEqualToString:@"POST"]) {
        if(! self.HTTPBody) { NSInteger maxLength = 1024; uint8_t d[maxLength]; NSInputStream *stream = self.HTTPBodyStream; NSMutableData *data = [[NSMutableData alloc] init]; [stream open]; BOOL endOfStreamReached = NO; [stream hasBytesAvailable]) will always return YES when processing image fileswhileThere's an endless loop.while(! endOfStreamReached) { NSInteger bytesRead = [streamread:d maxLength:maxLength];
                if(bytesRead == 0) {// At the end of the file read endofstreamreach = YES; }else if(bytesRead == -1) {// File read error endOfStreamReached = YES; }else if(stream.streamError == nil) { [data appendBytes:(void *)d length:bytesRead]; } } req.HTTPBody = [data copy]; [stream close]; }}return req;
}
@endCopy the code

Here’s the implementation I gave you, and notice that someone did it in the beginning:

- (void)cyl_handlePostRequestBody {
    if ([self.HTTPMethod isEqualToString:@"POST"]) {
        if(! self.HTTPBody) { uint8_t d[1024] = {0}; NSInputStream *stream = self.HTTPBodyStream; NSMutableData *data = [[NSMutableData alloc] init]; [stream open];while ([stream hasBytesAvailable]) {
                NSInteger len = [stream read:d maxLength:1024];
                if(len > 0 && stream.streamError == nil) { [data appendBytes:(void *)d length:len]; } } self.HTTPBody = [data copy]; [stream close]; }}}Copy the code

The problem with this implementation is that you can’t use [stream hasBytesAvailable]). When processing image files, [Stream hasBytesAvailable] will always return YES, resulting in an infinite loop in the while.

Apple’s documentation makes it very clear:

 // returns in O(1) a pointer to the buffer in 'buffer' and by reference in 'len' how many bytes are available. This buffer is only valid until the next stream operation. Subclassers may return NO for this if it is not appropriate for the stream type. This may return NO if the buffer is not available.
   @property (readonly) BOOL hasBytesAvailable;Copy the code

The implementation is given, and the use method is introduced below:

In used to intercept request NSURLProtocol subclass implementation method + canonicalRequestForRequest: and handle the request object:

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
   return [request cyl_getPostRequestIncludeBody];
}Copy the code

Here are the functions of relevant methods:

//NSURLProtocol.h

/*! 
   @method canInitWithRequest:
   @abstract This method determines whether this protocol can handle
   the given request.
   @discussion A concrete subclass should inspect the given request and
   determine whether or not the implementation can perform a load with
   that request. This is an abstract method. Sublasses must provide an
   implementation.
   @param request A request to inspect.
   @result YES if the protocol can handle the given request, NO ifnot. */ + (BOOL)canInitWithRequest:(NSURLRequest *)request; / *! @method canonicalRequestForRequest: @abstract This method returns a canonical version of the given request. @discussion It is up to each concrete protocol implementation to define what"canonical" means. However, a protocol should
   guarantee that the same input request always yields the same
   canonical form. Special consideration should be given when
   implementing this method since the canonical form of a request is
   used to look up objects in the URL cache, a process which performs
   equality checks between NSURLRequest objects.
   <p>
   This is an abstract method; sublasses must provide an
   implementation.
   @param request A request to make canonical.
   @result The canonical form of the given request. 
*/
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;Copy the code

Translation:

//NSURLProtocol.h /*! * method: Create an instance of NSURLProtocol. After NSURLProtocol is registered, all NSURlConnections will use this method to check whether they hold the Http request. @parma : @return: YES: hold the Http request NO: do not hold the Http request */ + (BOOL)canInitWithRequest:(NSURLRequest *) Request /*! * @method: The NSURLProtocol abstract class must be implemented. Usually there is a minimum standard: that is, input and output requests conform to the most basic protocol specifications. So the simple thing to do here is just go back. Normally we don't change this request. If you want to make changes, such as adding a title to the request, combine it into a new HTTP request. @parma: local HttpRequest request: request @return: direct forward * / + (NSURLRequest *) canonicalRequestForRequest (NSURLRequest *) requestCopy the code

Simple said.

  • +[NSURLProtocol canInitWithRequest:] is responsible for filtering which network requests need to be blocked
  • + [NSURLProtocol canonicalRequestForRequest:] is responsible for the need to intercept network request to reconstruct NSURLRequest.

There is a note: + [NSURLProtocol canonicalRequestForRequest:] terms of execution is + [NSURLProtocol canInitWithRequest:] the return value is YES.

Note in intercepting NSURLSession request, the need will be used to intercept the request NSURLProtocol subclasses of added to the NSURLSessionConfiguration, usage is as follows:

  NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSArray *protocolArray = @[ [CYLURLProtocol class] ];
    configuration.protocolClasses = protocolArray;
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]];Copy the code

Switch to a lower-level network library that provides an interface for SNI field configuration

If you use the curl library, you can use the -resolve method to access HTTPS websites using the specified IP address.

Note that it supports IPv6 as well, just add — enable-ipv6 to your build.

Curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl curl

If you want to access. www.example.org, if the IP is 127.0.0.1, then you can set SNI by calling it this way:

curl * --resolve 'www.example.org:443:127.0.0.1'Copy the code

IOS CURL library

To do this, use libcurl/cURL at least 7.18.1 (30 March 2008) to compile an SSL/TLS toolkit with SNI support. CURL has a — resolve method that can be used to access HTTPS sites using specified IP addresses.

In the iOS implementation, the code is as follows

//{HTTPS domain name}:443:{IP address} NSString *curlHost =... ; _hosts_list = curl_slist_append(_hosts_list, curlHost.UTF8String); curl_easy_setopt(_curl, CURLOPT_RESOLVE, _hosts_list);Copy the code

Where, curlHost looks like:

{HTTPS domain name}:443:{IP address}

_hosts_list is the structure type hosts_list. You can set the mapping between multiple IP addresses and hosts. The CURLOPT_RESOLVE is passed in the curl_easy_setopt method to set the mapping to the HTTPS request.

In this way, you can set SNI.

You can use curl to create a NSURLSession using curl. You can use curl to create an NSURLSession using curl.

Reference links:

Apple – Communicating with HTTP Servers

Apple – HTTPS Server Trust Evaluation – Server Name Failures

Apple-https Server Trust Evaluation – Confide One Specific Certificate HTTPDNS best Practices Describes how to Openly Confide One Specific Certificate with HTTPS (including SNI HTTPS (including SNI) Service Scenario Description of DIRECT IP Connection Using the specified IP address to Request HTTPS in curl Alicloud-ios-demo OF SNI and WebView SNI: Implementing SSL/TLS Authentication for Virtual Hosts with Multiple Domain names

added

Note that the discussion above does not cover the loss of the body in WKWebView that intercepts NSURLSession requests.

Several concepts mentioned in the article:



All domain names mentioned in this article, unless otherwise specified, refer to FQDN.

The original link

To read more articles, please scan the following QR code: