Let’s talk about the process of small program development, generally use the tool process real machine testing, so, is a little curious, is how to use the small program development tool package, and then send the packet to the mobile phone in the local area network? So, here is a simple implementation of iOS mobile phone internal build local server, to achieve data transmission function. That is, with a communication bridge, decompress the small program compression package and save it in the sandbox path, and preview it with the mobile phone small program SDK. This is just a speculations of how it will be implemented, and it’s not clear whether it’s official. However, here is the realization of iOS mobile phone internal implementation to build a server, HTTP and HTTPS access, data transmission.

First look at the renderings:

Step 1: FirstpodtheCocoaHTTPServerThis third party

Pod 'CocoaHTTPServer', '~ > 2.3'Copy the code

Step 2. ImplementationhttpRequest way

Create two classes

1. KDSHttpServer inherits from NSObject and is used to set, start, and stop services

@interface KDSHttpServer : NSObject

@end
Copy the code
#import "KDSHttpServer.h" #import <CocoaHTTPServer/HTTPServer.h> #import "KDSHTTPConnection.h" #include <ifaddrs.h> #include < ARPA /inet.h> @interface KDSHttpServer() @property (nonatomic,strong) HTTPServer * server; @property (nonatomic,strong) NSString * port; @end @implementation KDSHttpServer - (instancetype)init { self = [super init]; If (self) {// Get the current IP [self getIPAddress]; self.server = [[HTTPServer alloc] init]; // Set the fixed port number [self.server setPort:12345]; // Set the request type [self.server setType:@"_https.tcp"]; // Set the request management class (set the interface address request type (POST, GET), Set service HTTP/HTTPS) [self.server setConnectionClass:[KDSHTTPConnection class]]; // Start the service [self startServer]; } return self; } // Start service - (void)startServer {NSError *error; if([self.server start:&error]){ NSLog(@"Started HTTP Server on port %hu", [self.server listeningPort]); self.port = [NSString stringWithFormat:@"%d",[self.server listeningPort]]; // Save the port number and use NSUserDefaults *accountDefaults = [NSUserDefaults standardUserDefaults] when called; [accountDefaults setObject:self.port forKey:@"webPort"]; [accountDefaults synchronize]; } else { NSLog(@"Error starting HTTP Server: %@", error); } // get local IP address - (NSString *)getIPAddress {NSString *address = @"error"; struct ifaddrs *interfaces = NULL; struct ifaddrs *temp_addr = NULL; int success = 0; //retrieve the current interfaces - returns 0 on success success = getifaddrs(&interfaces); if (success == 0) { //Loop through linked list of interfaces temp_addr = interfaces; while (temp_addr ! = NULL) { if (temp_addr->ifa_addr->sa_family == AF_INET) { //Check if interface is en0 which is the wifi connection on the iPhone if ([[NSString stringWithUTF8String: temp_addr->ifa_name] isEqualToString:@"en0"]) { //Get NSString from C String address =[NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *) temp_addr->ifa_addr)->sin_addr)]; } } temp_addr = temp_addr->ifa_next; } } //Free memory freeifaddrs(interfaces); NSLog(@"addrees----%@",address); return address; } // Stop the server when the server is destroyed - (void)dealloc {[self.server stop]; }Copy the code

2. KDSHTTPConnection inherits from HTTPConnection class and defines the interface request type and server HTTP/HTTPS protocol type.

#import <Foundation/Foundation.h>
#import <CocoaHTTPServer/MultipartFormDataParser.h>
#import <CocoaHTTPServer/HTTPConnection.h>
#import <CocoaHTTPServer/HTTPDataResponse.h>
#import <CocoaHTTPServer/HTTPMessage.h>

NS_ASSUME_NONNULL_BEGIN

@interface KDSHTTPConnection : HTTPConnection<MultipartFormDataParserDelegate>

@end

NS_ASSUME_NONNULL_END
Copy the code

Follow MultipartFormDataParserDelegate agency agreement may constraint form of request.

// Request modes supported by the interface path (POST, GET...) - (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path; // Whether the body is allowed to carry parameters, (the default POST, PUT, is to allow) - (BOOL) expectsRequestBodyFromMethod: (nsstrings *) method atPath (nsstrings *) path; // Accept the request response, - (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path; - (void)processBodyData:(NSData *)postDataChunk; (void)processBodyData:(NSData *)postDataChunk; // Whether HTTPS is supported (YES indicates HTTPS) - (BOOL)isSecureServer; // Return a set of certificates. The first set of the array must be SecIdentityRef, followed by cer certificates. - (NSArray *)sslIdentityAndCertificates;Copy the code

The following is the HTTP establishment, the.m code is as follows:

- (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path {// Add support for POST if ([method isEqualToString:@"POST"]) { if ([path isEqualToString:@"/doPost"]) { // Let's be extra cautious, and make sure the upload isn't 5 gigs return YES; } } if ([method isEqualToString:@"GET"]) { if ([path isEqualToString:@"/doGet"]) { // Let's be extra cautious, and make sure the upload isn't 5 gigs return YES; } } return [super supportsMethod:method atPath:path]; } / / whether to allow the body to carry parameters - (BOOL) expectsRequestBodyFromMethod: (nsstrings *) method atPath path: (nsstrings *) {/ / Inform HTTP server that we expect a body to accompany a POST request if([method isEqualToString:@"POST"]) return YES; return [super expectsRequestBodyFromMethod:method atPath:path]; } - (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path { if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/doPost"]) { [self parseRequest]; NSDictionary * responsDic = @ {@ "code" : @ "200", @ "MSG" : @ "successful"}; NSData *responseData = [NSJSONSerialization dataWithJSONObject:responsDic options:0 error:nil]; return [[HTTPDataResponse alloc] initWithData:responseData]; } if ([method isEqualToString:@"GET"]) { NSLog(@"GET param = %@",path); //[self getRequestParam:[request messageData] method:method]; NSDictionary * responsDic = @ {@ "code" : @ "200", @ "MSG" : @ "successful"}; NSData *responseData = [NSJSONSerialization dataWithJSONObject:responsDic options:0 error:**nil**]; return [[HTTPDataResponse alloc] initWithData:responseData]; } return [super httpResponseForMethod:method URI:path]; } / / parse the POST data (the GET parameter data is through the address came, so don't do too much here, matching a string) - (void) parseRequest {[self getRequestParam: request. The body]. } - (NSDictionary *)getRequestParam:(NSData *)rawData {if (! rawData) return nil; NSString *raw = [[NSString alloc] initWithData:rawData encoding:NSUTF8StringEncoding]; NSMutableDictionary *paramDic = [NSMutableDictionary dictionary]; NSArray *array = [raw componentsSeparatedByString:@"&"]; for (NSString *string in array) { NSArray *arr = [string componentsSeparatedByString:@"="]; NSString *value = [arr.lastObject stringByRemovingPercentEncoding]; if (value && value.length) { [paramDic setValue:value forKey:arr.firstObject]; } } NSLog(@"POST param = %@",paramDic); return [paramDic copy]; }Copy the code

Ok, run it:

Self. httpServer = [[KDSHttpServer alloc] init];Copy the code

Demo will remind the firewall whether to allow access to the network, here click permit.

Check the address and port of the phone’s local server

Run with the Postman tool

As you can see, the return is successful. Notice that the data upload format in the body is different, and the local server parses the code accordingly.

Xcode console output:

Okay, now that the HTTP request is over, let’s set up the HTTPS request

Step 3. ImplementationhttpsRequest way

Kdshttpconnection. m file to enable HTTPS function, the code is as follows:

// HTTPS allowed - (BOOL)isSecureServer {return YES; } / / return the related certificate (a collection of the first must be SecIdentityRef) - (NSArray *) sslIdentityAndCertificates {SecIdentityRef identityRef = NULL; SecCertificateRef certificateRef = NULL; SecTrustRef trustRef = NULL; NSString *thePath = [[NSBundle mainBundle] pathForResource:@"private_key" ofType:@"p12"]; NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath]; CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(PKCS12Data); //p12 file password CFStringRef password = CFSTR("123456"); const void *keys[] = { kSecImportExportPassphrase }; const void *values[] = { password }; CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL); CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL); OSStatus securityError = errSecSuccess; securityError = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items); if (securityError == 0) { CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0); const void *tempIdentity = NULL; tempIdentity = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity); IdentityRef = (SecIdentityRef)tempIdentity; const void *tempTrust = NULL; tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust); trustRef = (SecTrustRef)tempTrust; } else { NSLog(@"Failed with error code %d",(int)securityError); return nil; } / / signing certificate SecIdentityCopyCertificate (identityRef, & certificateRef); NSArray *result = [[NSArray alloc] initWithObjects:(id)CFBridgingRelease(identityRef), (id)CFBridgingRelease(certificateRef), nil]; return result; }Copy the code

P12 file terminal command:

openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt
Copy the code

The directory structure in the project is as follows:

Other files generated refer to # iOS simply emulating HTTPS certificate trust logic.

Postman visits the address

The third party GCDAsynScoket reported an error

The rough and ready way, just comment it out

Let’s go to Postman again,

As you can see here, the postman prompt is from the visa, but it doesn’t block the request and still returns the data.

So, afnetwoking for a visit:

// The initializer must be baseUrl, Domain name authentication requires AFHTTPSessionManager * m = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString: @ "https://10.10.60.20"]]; NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"rsaCert" ofType:@"cer"]; / / certificate path xx. Cer NSData * cerData = [NSData dataWithContentsOfFile: cerPath]; AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate]; securityPolicy.pinnedCertificates = [[NSSet alloc] initWithObjects:cerData, nil]; / / whether to allow invalid certificate, the default is NO securityPolicy. AllowInvalidCertificates = NO; / / check the domain name, the default value is YES securityPolicy. ValidatesDomainName = NO. [m setSecurityPolicy:securityPolicy]; m.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/html",@"text/css",@"text/plain",@"application/xhtml+xml",@"application/xml", nil]; NSDictionary * dic = @{@"title":@"123"}; //NSLog(@"fileSize = %llu",[dic fileSize]); [m POST: @ "https://10.10.60.20:12345/doPost" parameters: dic headers: nil progress: ^ (NSProgress * _Nonnull downloadProgress) { } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { NSLog(@"responseObject = %@",responseObject);  } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { }];Copy the code

Other files generated refer to # iOS simply emulating HTTPS certificate trust logic.

Due to the above request does not allow the visa book (securityPolicy. ValidatesDomainName = NO), so, AFNetwoeking request must fail:

The securityPolicy. ValidatesDomainName set to YES, then visit:

As you can see, the authentication is successful and the correct return value is output.

If this parameter is set to YES, Common Name must be entered correctly when creating the certificate.

Well, here to build mobile phone internal server, HTTP and HTTPS access, data transmission function is completed, on such a basis can not do a lot of things? Don’t laugh at bad code.