open source
@@ -0,0 +1,41 @@
|
||||
.svn
|
||||
Redist/appCache
|
||||
*.sdf
|
||||
*.ncb
|
||||
*.suo
|
||||
*.vcproj.*
|
||||
*.vcxproj.user
|
||||
ipch
|
||||
debug.txt
|
||||
*.opensdf
|
||||
*.orig
|
||||
*.rej
|
||||
*.obj
|
||||
*.ilk
|
||||
*.pdb
|
||||
debug/*.tlog
|
||||
debug/*.log
|
||||
debug/*.pch
|
||||
debug/*.lastbuildstate
|
||||
debug/*.res
|
||||
*.idb
|
||||
release/*.tlog
|
||||
release/*.log
|
||||
release/*.pch
|
||||
release/*.lastbuildstate
|
||||
release/*.res
|
||||
.DS_Store
|
||||
*.tlog
|
||||
Intermediate
|
||||
*.opendb
|
||||
intermediates
|
||||
.idea
|
||||
gdbserver
|
||||
gdb.setup
|
||||
*.bin
|
||||
libcommon.a
|
||||
librender.a
|
||||
liblaya.so
|
||||
.gradle
|
||||
*.d
|
||||
*.bc
|
||||
@@ -0,0 +1,6 @@
|
||||
[submodule "ThirdParty/openssl"]
|
||||
path = ThirdParty/openssl
|
||||
url = https://github.com/openssl/openssl.git
|
||||
[submodule "ThirdParty/msgpack-c"]
|
||||
path = ThirdParty/msgpack-c
|
||||
url = https://github.com/msgpack/msgpack-c.git
|
||||
@@ -0,0 +1,622 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
|
||||
#import "GCDWebServerRequest.h"
|
||||
#import "GCDWebServerResponse.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerMatchBlock is called for every handler added to the
|
||||
* GCDWebServer whenever a new HTTP request has started (i.e. HTTP headers have
|
||||
* been received). The block is passed the basic info for the request (HTTP method,
|
||||
* URL, headers...) and must decide if it wants to handle it or not.
|
||||
*
|
||||
* If the handler can handle the request, the block must return a new
|
||||
* GCDWebServerRequest instance created with the same basic info.
|
||||
* Otherwise, it simply returns nil.
|
||||
*/
|
||||
typedef GCDWebServerRequest* _Nullable (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery);
|
||||
|
||||
/**
|
||||
* The GCDWebServerProcessBlock is called after the HTTP request has been fully
|
||||
* received (i.e. the entire HTTP body has been read). The block is passed the
|
||||
* GCDWebServerRequest created at the previous step by the GCDWebServerMatchBlock.
|
||||
*
|
||||
* The block must return a GCDWebServerResponse or nil on error, which will
|
||||
* result in a 500 HTTP status code returned to the client. It's however
|
||||
* recommended to return a GCDWebServerErrorResponse on error so more useful
|
||||
* information can be returned to the client.
|
||||
*/
|
||||
typedef GCDWebServerResponse* _Nullable (^GCDWebServerProcessBlock)(__kindof GCDWebServerRequest* request);
|
||||
|
||||
/**
|
||||
* The GCDWebServerAsynchronousProcessBlock works like the GCDWebServerProcessBlock
|
||||
* except the GCDWebServerResponse can be returned to the server at a later time
|
||||
* allowing for asynchronous generation of the response.
|
||||
*
|
||||
* The block must eventually call "completionBlock" passing a GCDWebServerResponse
|
||||
* or nil on error, which will result in a 500 HTTP status code returned to the client.
|
||||
* It's however recommended to return a GCDWebServerErrorResponse on error so more
|
||||
* useful information can be returned to the client.
|
||||
*/
|
||||
typedef void (^GCDWebServerCompletionBlock)(GCDWebServerResponse* _Nullable response);
|
||||
typedef void (^GCDWebServerAsyncProcessBlock)(__kindof GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock);
|
||||
|
||||
/**
|
||||
* The port used by the GCDWebServer (NSNumber / NSUInteger).
|
||||
*
|
||||
* The default value is 0 i.e. let the OS pick a random port.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_Port;
|
||||
|
||||
/**
|
||||
* The Bonjour name used by the GCDWebServer (NSString). If set to an empty string,
|
||||
* the name will automatically take the value of the GCDWebServerOption_ServerName
|
||||
* option. If this option is set to nil, Bonjour will be disabled.
|
||||
*
|
||||
* The default value is nil.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_BonjourName;
|
||||
|
||||
/**
|
||||
* The Bonjour service type used by the GCDWebServer (NSString).
|
||||
*
|
||||
* The default value is "_http._tcp", the service type for HTTP web servers.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_BonjourType;
|
||||
|
||||
/**
|
||||
* Request a port mapping in the NAT gateway (NSNumber / BOOL).
|
||||
*
|
||||
* This uses the DNSService API under the hood which supports IPv4 mappings only.
|
||||
*
|
||||
* The default value is NO.
|
||||
*
|
||||
* @warning The external port set up by the NAT gateway may be different than
|
||||
* the one used by the GCDWebServer.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_RequestNATPortMapping;
|
||||
|
||||
/**
|
||||
* Only accept HTTP requests coming from localhost i.e. not from the outside
|
||||
* network (NSNumber / BOOL).
|
||||
*
|
||||
* The default value is NO.
|
||||
*
|
||||
* @warning Bonjour and NAT port mapping should be disabled if using this option
|
||||
* since the server will not be reachable from the outside network anyway.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_BindToLocalhost;
|
||||
|
||||
/**
|
||||
* The maximum number of incoming HTTP requests that can be queued waiting to
|
||||
* be handled before new ones are dropped (NSNumber / NSUInteger).
|
||||
*
|
||||
* The default value is 16.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_MaxPendingConnections;
|
||||
|
||||
/**
|
||||
* The value for "Server" HTTP header used by the GCDWebServer (NSString).
|
||||
*
|
||||
* The default value is the GCDWebServer class name.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_ServerName;
|
||||
|
||||
/**
|
||||
* The authentication method used by the GCDWebServer
|
||||
* (one of "GCDWebServerAuthenticationMethod_...").
|
||||
*
|
||||
* The default value is nil i.e. authentication is disabled.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_AuthenticationMethod;
|
||||
|
||||
/**
|
||||
* The authentication realm used by the GCDWebServer (NSString).
|
||||
*
|
||||
* The default value is the same as the GCDWebServerOption_ServerName option.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_AuthenticationRealm;
|
||||
|
||||
/**
|
||||
* The authentication accounts used by the GCDWebServer
|
||||
* (NSDictionary of username / password pairs).
|
||||
*
|
||||
* The default value is nil i.e. no accounts.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_AuthenticationAccounts;
|
||||
|
||||
/**
|
||||
* The class used by the GCDWebServer when instantiating GCDWebServerConnection
|
||||
* (subclass of GCDWebServerConnection).
|
||||
*
|
||||
* The default value is the GCDWebServerConnection class.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_ConnectionClass;
|
||||
|
||||
/**
|
||||
* Allow the GCDWebServer to pretend "HEAD" requests are actually "GET" ones
|
||||
* and automatically discard the HTTP body of the response (NSNumber / BOOL).
|
||||
*
|
||||
* The default value is YES.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET;
|
||||
|
||||
/**
|
||||
* The interval expressed in seconds used by the GCDWebServer to decide how to
|
||||
* coalesce calls to -webServerDidConnect: and -webServerDidDisconnect:
|
||||
* (NSNumber / double). Coalescing will be disabled if the interval is <= 0.0.
|
||||
*
|
||||
* The default value is 1.0 second.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval;
|
||||
|
||||
/**
|
||||
* Set the dispatch queue priority on which server connection will be
|
||||
* run (NSNumber / long).
|
||||
*
|
||||
*
|
||||
* The default value is DISPATCH_QUEUE_PRIORITY_DEFAULT.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_DispatchQueuePriority;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
/**
|
||||
* Enables the GCDWebServer to automatically suspend itself (as if -stop was
|
||||
* called) when the iOS app goes into the background and the last
|
||||
* GCDWebServerConnection is closed, then resume itself (as if -start was called)
|
||||
* when the iOS app comes back to the foreground (NSNumber / BOOL).
|
||||
*
|
||||
* See the README.md file for more information about this option.
|
||||
*
|
||||
* The default value is YES.
|
||||
*
|
||||
* @warning The running property will be NO while the GCDWebServer is suspended.
|
||||
*/
|
||||
extern NSString* const GCDWebServerOption_AutomaticallySuspendInBackground;
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* HTTP Basic Authentication scheme (see https://tools.ietf.org/html/rfc2617).
|
||||
*
|
||||
* @warning Use of this authentication scheme is not recommended as the
|
||||
* passwords are sent in clear.
|
||||
*/
|
||||
extern NSString* const GCDWebServerAuthenticationMethod_Basic;
|
||||
|
||||
/**
|
||||
* HTTP Digest Access Authentication scheme (see https://tools.ietf.org/html/rfc2617).
|
||||
*/
|
||||
extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
|
||||
@class GCDWebServer;
|
||||
|
||||
/**
|
||||
* Delegate methods for GCDWebServer.
|
||||
*
|
||||
* @warning These methods are always called on the main thread in a serialized way.
|
||||
*/
|
||||
@protocol GCDWebServerDelegate <NSObject>
|
||||
@optional
|
||||
|
||||
/**
|
||||
* This method is called after the server has successfully started.
|
||||
*/
|
||||
- (void)webServerDidStart:(GCDWebServer*)server;
|
||||
|
||||
/**
|
||||
* This method is called after the Bonjour registration for the server has
|
||||
* successfully completed.
|
||||
*
|
||||
* Use the "bonjourServerURL" property to retrieve the Bonjour address of the
|
||||
* server.
|
||||
*/
|
||||
- (void)webServerDidCompleteBonjourRegistration:(GCDWebServer*)server;
|
||||
|
||||
/**
|
||||
* This method is called after the NAT port mapping for the server has been
|
||||
* updated.
|
||||
*
|
||||
* Use the "publicServerURL" property to retrieve the public address of the
|
||||
* server.
|
||||
*/
|
||||
- (void)webServerDidUpdateNATPortMapping:(GCDWebServer*)server;
|
||||
|
||||
/**
|
||||
* This method is called when the first GCDWebServerConnection is opened by the
|
||||
* server to serve a series of HTTP requests.
|
||||
*
|
||||
* A series of HTTP requests is considered ongoing as long as new HTTP requests
|
||||
* keep coming (and new GCDWebServerConnection instances keep being opened),
|
||||
* until before the last HTTP request has been responded to (and the
|
||||
* corresponding last GCDWebServerConnection closed).
|
||||
*/
|
||||
- (void)webServerDidConnect:(GCDWebServer*)server;
|
||||
|
||||
/**
|
||||
* This method is called when the last GCDWebServerConnection is closed after
|
||||
* the server has served a series of HTTP requests.
|
||||
*
|
||||
* The GCDWebServerOption_ConnectedStateCoalescingInterval option can be used
|
||||
* to have the server wait some extra delay before considering that the series
|
||||
* of HTTP requests has ended (in case there some latency between consecutive
|
||||
* requests). This effectively coalesces the calls to -webServerDidConnect:
|
||||
* and -webServerDidDisconnect:.
|
||||
*/
|
||||
- (void)webServerDidDisconnect:(GCDWebServer*)server;
|
||||
|
||||
/**
|
||||
* This method is called after the server has stopped.
|
||||
*/
|
||||
- (void)webServerDidStop:(GCDWebServer*)server;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The GCDWebServer class listens for incoming HTTP requests on a given port,
|
||||
* then passes each one to a "handler" capable of generating an HTTP response
|
||||
* for it, which is then sent back to the client.
|
||||
*
|
||||
* GCDWebServer instances can be created and used from any thread but it's
|
||||
* recommended to have the main thread's runloop be running so internal callbacks
|
||||
* can be handled e.g. for Bonjour registration.
|
||||
*
|
||||
* See the README.md file for more information about the architecture of GCDWebServer.
|
||||
*/
|
||||
@interface GCDWebServer : NSObject
|
||||
|
||||
/**
|
||||
* Sets the delegate for the server.
|
||||
*/
|
||||
@property(nonatomic, weak, nullable) id<GCDWebServerDelegate> delegate;
|
||||
|
||||
/**
|
||||
* Returns YES if the server is currently running.
|
||||
*/
|
||||
@property(nonatomic, readonly, getter=isRunning) BOOL running;
|
||||
|
||||
/**
|
||||
* Returns the port used by the server.
|
||||
*
|
||||
* @warning This property is only valid if the server is running.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSUInteger port;
|
||||
|
||||
/**
|
||||
* Returns the Bonjour name used by the server.
|
||||
*
|
||||
* @warning This property is only valid if the server is running and Bonjour
|
||||
* registration has successfully completed, which can take up to a few seconds.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString* bonjourName;
|
||||
|
||||
/**
|
||||
* Returns the Bonjour service type used by the server.
|
||||
*
|
||||
* @warning This property is only valid if the server is running and Bonjour
|
||||
* registration has successfully completed, which can take up to a few seconds.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString* bonjourType;
|
||||
|
||||
/**
|
||||
* This method is the designated initializer for the class.
|
||||
*/
|
||||
- (instancetype)init;
|
||||
|
||||
/**
|
||||
* Adds to the server a handler that generates responses synchronously when handling incoming HTTP requests.
|
||||
*
|
||||
* Handlers are called in a LIFO queue, so if multiple handlers can potentially
|
||||
* respond to a given request, the latest added one wins.
|
||||
*
|
||||
* @warning Addling handlers while the server is running is not allowed.
|
||||
*/
|
||||
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
|
||||
|
||||
/**
|
||||
* Adds to the server a handler that generates responses asynchronously when handling incoming HTTP requests.
|
||||
*
|
||||
* Handlers are called in a LIFO queue, so if multiple handlers can potentially
|
||||
* respond to a given request, the latest added one wins.
|
||||
*
|
||||
* @warning Addling handlers while the server is running is not allowed.
|
||||
*/
|
||||
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock;
|
||||
|
||||
/**
|
||||
* Removes all handlers previously added to the server.
|
||||
*
|
||||
* @warning Removing handlers while the server is running is not allowed.
|
||||
*/
|
||||
- (void)removeAllHandlers;
|
||||
|
||||
/**
|
||||
* Starts the server with explicit options. This method is the designated way
|
||||
* to start the server.
|
||||
*
|
||||
* Returns NO if the server failed to start and sets "error" argument if not NULL.
|
||||
*/
|
||||
- (BOOL)startWithOptions:(nullable NSDictionary*)options error:(NSError** _Nullable)error;
|
||||
|
||||
/**
|
||||
* Stops the server and prevents it to accepts new HTTP requests.
|
||||
*
|
||||
* @warning Stopping the server does not abort GCDWebServerConnection instances
|
||||
* currently handling already received HTTP requests. These connections will
|
||||
* continue to execute normally until completion.
|
||||
*/
|
||||
- (void)stop;
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServer (Extensions)
|
||||
|
||||
/**
|
||||
* Returns the server's URL.
|
||||
*
|
||||
* @warning This property is only valid if the server is running.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL* serverURL;
|
||||
|
||||
/**
|
||||
* Returns the server's Bonjour URL.
|
||||
*
|
||||
* @warning This property is only valid if the server is running and Bonjour
|
||||
* registration has successfully completed, which can take up to a few seconds.
|
||||
* Also be aware this property will not automatically update if the Bonjour hostname
|
||||
* has been dynamically changed after the server started running (this should be rare).
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL* bonjourServerURL;
|
||||
|
||||
/**
|
||||
* Returns the server's public URL.
|
||||
*
|
||||
* @warning This property is only valid if the server is running and NAT port
|
||||
* mapping is active.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSURL* publicServerURL;
|
||||
|
||||
/**
|
||||
* Starts the server on port 8080 (OS X & iOS Simulator) or port 80 (iOS)
|
||||
* using the default Bonjour name.
|
||||
*
|
||||
* Returns NO if the server failed to start.
|
||||
*/
|
||||
- (BOOL)start;
|
||||
|
||||
/**
|
||||
* Starts the server on a given port and with a specific Bonjour name.
|
||||
* Pass a nil Bonjour name to disable Bonjour entirely or an empty string to
|
||||
* use the default name.
|
||||
*
|
||||
* Returns NO if the server failed to start.
|
||||
*/
|
||||
- (BOOL)startWithPort:(NSUInteger)port bonjourName:(nullable NSString*)name;
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
|
||||
/**
|
||||
* Runs the server synchronously using -startWithPort:bonjourName: until a
|
||||
* SIGINT signal is received i.e. Ctrl-C. This method is intended to be used
|
||||
* by command line tools.
|
||||
*
|
||||
* Returns NO if the server failed to start.
|
||||
*
|
||||
* @warning This method must be used from the main thread only.
|
||||
*/
|
||||
- (BOOL)runWithPort:(NSUInteger)port bonjourName:(nullable NSString*)name;
|
||||
|
||||
/**
|
||||
* Runs the server synchronously using -startWithOptions: until a SIGTERM or
|
||||
* SIGINT signal is received i.e. Ctrl-C in Terminal. This method is intended to
|
||||
* be used by command line tools.
|
||||
*
|
||||
* Returns NO if the server failed to start and sets "error" argument if not NULL.
|
||||
*
|
||||
* @warning This method must be used from the main thread only.
|
||||
*/
|
||||
- (BOOL)runWithOptions:(nullable NSDictionary*)options error:(NSError** _Nullable)error;
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServer (Handlers)
|
||||
|
||||
/**
|
||||
* Adds a default handler to the server to handle all incoming HTTP requests
|
||||
* with a given HTTP method and generate responses synchronously.
|
||||
*/
|
||||
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block;
|
||||
|
||||
/**
|
||||
* Adds a default handler to the server to handle all incoming HTTP requests
|
||||
* with a given HTTP method and generate responses asynchronously.
|
||||
*/
|
||||
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block;
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to handle incoming HTTP requests with a given
|
||||
* HTTP method and a specific case-insensitive path and generate responses
|
||||
* synchronously.
|
||||
*/
|
||||
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block;
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to handle incoming HTTP requests with a given
|
||||
* HTTP method and a specific case-insensitive path and generate responses
|
||||
* asynchronously.
|
||||
*/
|
||||
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block;
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to handle incoming HTTP requests with a given
|
||||
* HTTP method and a path matching a case-insensitive regular expression and
|
||||
* generate responses synchronously.
|
||||
*/
|
||||
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block;
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to handle incoming HTTP requests with a given
|
||||
* HTTP method and a path matching a case-insensitive regular expression and
|
||||
* generate responses asynchronously.
|
||||
*/
|
||||
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block;
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServer (GETHandlers)
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to respond to incoming "GET" HTTP requests
|
||||
* with a specific case-insensitive path with in-memory data.
|
||||
*/
|
||||
- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(nullable NSString*)contentType cacheAge:(NSUInteger)cacheAge;
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to respond to incoming "GET" HTTP requests
|
||||
* with a specific case-insensitive path with a file.
|
||||
*/
|
||||
- (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests;
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to respond to incoming "GET" HTTP requests
|
||||
* with a case-insensitive path inside a base path with the corresponding file
|
||||
* inside a local directory. If no local file matches the request path, a 401
|
||||
* HTTP status code is returned to the client.
|
||||
*
|
||||
* The "indexFilename" argument allows to specify an "index" file name to use
|
||||
* when the request path corresponds to a directory.
|
||||
*/
|
||||
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(nullable NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* GCDWebServer provides its own built-in logging facility which is used by
|
||||
* default. It simply sends log messages to stderr assuming it is connected
|
||||
* to a terminal type device.
|
||||
*
|
||||
* GCDWebServer is also compatible with a limited set of third-party logging
|
||||
* facilities. If one of them is available at compile time, GCDWebServer will
|
||||
* automatically use it in place of the built-in one.
|
||||
*
|
||||
* Currently supported third-party logging facilities are:
|
||||
* - XLFacility (by the same author as GCDWebServer): https://github.com/swisspol/XLFacility
|
||||
*
|
||||
* For the built-in logging facility, the default logging level is INFO
|
||||
* (or DEBUG if the preprocessor constant "DEBUG" evaluates to non-zero at
|
||||
* compile time).
|
||||
*
|
||||
* It's possible to have GCDWebServer use a custom logging facility by defining
|
||||
* the "__GCDWEBSERVER_LOGGING_HEADER__" preprocessor constant in Xcode build
|
||||
* settings to the name of a custom header file (escaped like \"MyLogging.h\").
|
||||
* This header file must define the following set of macros:
|
||||
*
|
||||
* GWS_LOG_DEBUG(...)
|
||||
* GWS_LOG_VERBOSE(...)
|
||||
* GWS_LOG_INFO(...)
|
||||
* GWS_LOG_WARNING(...)
|
||||
* GWS_LOG_ERROR(...)
|
||||
*
|
||||
* IMPORTANT: These macros must behave like NSLog(). Furthermore the GWS_LOG_DEBUG()
|
||||
* macro should not do anything unless the preprocessor constant "DEBUG" evaluates
|
||||
* to non-zero.
|
||||
*
|
||||
* The logging methods below send log messages to the same logging facility
|
||||
* used by GCDWebServer. They can be used for consistency wherever you interact
|
||||
* with GCDWebServer in your code (e.g. in the implementation of handlers).
|
||||
*/
|
||||
@interface GCDWebServer (Logging)
|
||||
|
||||
/**
|
||||
* Sets the log level of the logging facility below which log messages are discarded.
|
||||
*
|
||||
* @warning The interpretation of the "level" argument depends on the logging
|
||||
* facility used at compile time.
|
||||
*
|
||||
* If using the built-in logging facility, the log levels are as follow:
|
||||
* DEBUG = 0
|
||||
* VERBOSE = 1
|
||||
* INFO = 2
|
||||
* WARNING = 3
|
||||
* ERROR = 4
|
||||
*/
|
||||
+ (void)setLogLevel:(int)level;
|
||||
|
||||
/**
|
||||
* Logs a message to the logging facility at the VERBOSE level.
|
||||
*/
|
||||
- (void)logVerbose:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
/**
|
||||
* Logs a message to the logging facility at the INFO level.
|
||||
*/
|
||||
- (void)logInfo:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
/**
|
||||
* Logs a message to the logging facility at the WARNING level.
|
||||
*/
|
||||
- (void)logWarning:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
/**
|
||||
* Logs a message to the logging facility at the ERROR level.
|
||||
*/
|
||||
- (void)logError:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
@end
|
||||
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
|
||||
@interface GCDWebServer (Testing)
|
||||
|
||||
/**
|
||||
* Activates recording of HTTP requests and responses which create files in the
|
||||
* current directory containing the raw data for all requests and responses.
|
||||
*
|
||||
* @warning The current directory must not contain any prior recording files.
|
||||
*/
|
||||
@property(nonatomic, getter=isRecordingEnabled) BOOL recordingEnabled;
|
||||
|
||||
/**
|
||||
* Runs tests by playing back pre-recorded HTTP requests in the given directory
|
||||
* and comparing the generated responses with the pre-recorded ones.
|
||||
*
|
||||
* Returns the number of failed tests or -1 if server failed to start.
|
||||
*/
|
||||
- (NSInteger)runTestsWithOptions:(nullable NSDictionary*)options inDirectory:(NSString*)path;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServer.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class GCDWebServerHandler;
|
||||
|
||||
/**
|
||||
* The GCDWebServerConnection class is instantiated by GCDWebServer to handle
|
||||
* each new HTTP connection. Each instance stays alive until the connection is
|
||||
* closed.
|
||||
*
|
||||
* You cannot use this class directly, but it is made public so you can
|
||||
* subclass it to override some hooks. Use the GCDWebServerOption_ConnectionClass
|
||||
* option for GCDWebServer to install your custom subclass.
|
||||
*
|
||||
* @warning The GCDWebServerConnection retains the GCDWebServer until the
|
||||
* connection is closed.
|
||||
*/
|
||||
@interface GCDWebServerConnection : NSObject
|
||||
|
||||
/**
|
||||
* Returns the GCDWebServer that owns the connection.
|
||||
*/
|
||||
@property(nonatomic, readonly) GCDWebServer* server;
|
||||
|
||||
/**
|
||||
* Returns YES if the connection is using IPv6.
|
||||
*/
|
||||
@property(nonatomic, readonly, getter=isUsingIPv6) BOOL usingIPv6;
|
||||
|
||||
/**
|
||||
* Returns the address of the local peer (i.e. server) of the connection
|
||||
* as a raw "struct sockaddr".
|
||||
*/
|
||||
@property(nonatomic, readonly) NSData* localAddressData;
|
||||
|
||||
/**
|
||||
* Returns the address of the local peer (i.e. server) of the connection
|
||||
* as a string.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* localAddressString;
|
||||
|
||||
/**
|
||||
* Returns the address of the remote peer (i.e. client) of the connection
|
||||
* as a raw "struct sockaddr".
|
||||
*/
|
||||
@property(nonatomic, readonly) NSData* remoteAddressData;
|
||||
|
||||
/**
|
||||
* Returns the address of the remote peer (i.e. client) of the connection
|
||||
* as a string.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* remoteAddressString;
|
||||
|
||||
/**
|
||||
* Returns the total number of bytes received from the remote peer (i.e. client)
|
||||
* so far.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSUInteger totalBytesRead;
|
||||
|
||||
/**
|
||||
* Returns the total number of bytes sent to the remote peer (i.e. client) so far.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSUInteger totalBytesWritten;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Hooks to customize the behavior of GCDWebServer HTTP connections.
|
||||
*
|
||||
* @warning These methods can be called on any GCD thread.
|
||||
* Be sure to also call "super" when overriding them.
|
||||
*/
|
||||
@interface GCDWebServerConnection (Subclassing)
|
||||
|
||||
/**
|
||||
* This method is called when the connection is opened.
|
||||
*
|
||||
* Return NO to reject the connection e.g. after validating the local
|
||||
* or remote address.
|
||||
*/
|
||||
- (BOOL)open;
|
||||
|
||||
/**
|
||||
* This method is called whenever data has been received
|
||||
* from the remote peer (i.e. client).
|
||||
*
|
||||
* @warning Do not attempt to modify this data.
|
||||
*/
|
||||
- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length;
|
||||
|
||||
/**
|
||||
* This method is called whenever data has been sent
|
||||
* to the remote peer (i.e. client).
|
||||
*
|
||||
* @warning Do not attempt to modify this data.
|
||||
*/
|
||||
- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length;
|
||||
|
||||
/**
|
||||
* This method is called after the HTTP headers have been received to
|
||||
* allow replacing the request URL by another one.
|
||||
*
|
||||
* The default implementation returns the original URL.
|
||||
*/
|
||||
- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary*)headers;
|
||||
|
||||
/**
|
||||
* Assuming a valid HTTP request was received, this method is called before
|
||||
* the request is processed.
|
||||
*
|
||||
* Return a non-nil GCDWebServerResponse to bypass the request processing entirely.
|
||||
*
|
||||
* The default implementation checks for HTTP authentication if applicable
|
||||
* and returns a barebone 401 status code response if authentication failed.
|
||||
*/
|
||||
- (nullable GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request;
|
||||
|
||||
/**
|
||||
* Assuming a valid HTTP request was received and -preflightRequest: returned nil,
|
||||
* this method is called to process the request by executing the handler's
|
||||
* process block.
|
||||
*/
|
||||
- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
* Assuming a valid HTTP request was received and either -preflightRequest:
|
||||
* or -processRequest:completion: returned a non-nil GCDWebServerResponse,
|
||||
* this method is called to override the response.
|
||||
*
|
||||
* You can either modify the current response and return it, or return a
|
||||
* completely new one.
|
||||
*
|
||||
* The default implementation replaces any response matching the "ETag" or
|
||||
* "Last-Modified-Date" header of the request by a barebone "Not-Modified" (304)
|
||||
* one.
|
||||
*/
|
||||
- (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request;
|
||||
|
||||
/**
|
||||
* This method is called if any error happens while validing or processing
|
||||
* the request or if no GCDWebServerResponse was generated during processing.
|
||||
*
|
||||
* @warning If the request was invalid (e.g. the HTTP headers were malformed),
|
||||
* the "request" argument will be nil.
|
||||
*/
|
||||
- (void)abortRequest:(nullable GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode;
|
||||
|
||||
/**
|
||||
* Called when the connection is closed.
|
||||
*/
|
||||
- (void)close;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,871 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
#import <netdb.h>
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
#import <libkern/OSAtomic.h>
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
#define kHeadersReadCapacity (1 * 1024)
|
||||
#define kBodyReadCapacity (256 * 1024)
|
||||
|
||||
typedef void (^ReadDataCompletionBlock)(BOOL success);
|
||||
typedef void (^ReadHeadersCompletionBlock)(NSData* extraData);
|
||||
typedef void (^ReadBodyCompletionBlock)(BOOL success);
|
||||
|
||||
typedef void (^WriteDataCompletionBlock)(BOOL success);
|
||||
typedef void (^WriteHeadersCompletionBlock)(BOOL success);
|
||||
typedef void (^WriteBodyCompletionBlock)(BOOL success);
|
||||
|
||||
static NSData* _CRLFData = nil;
|
||||
static NSData* _CRLFCRLFData = nil;
|
||||
static NSData* _continueData = nil;
|
||||
static NSData* _lastChunkData = nil;
|
||||
static NSString* _digestAuthenticationNonce = nil;
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
static int32_t _connectionCounter = 0;
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface GCDWebServerConnection (Read)
|
||||
- (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block;
|
||||
- (void)readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block;
|
||||
- (void)readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block;
|
||||
- (void)readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerConnection (Write)
|
||||
- (void)writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block;
|
||||
- (void)writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block;
|
||||
- (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@implementation GCDWebServerConnection {
|
||||
CFSocketNativeHandle _socket;
|
||||
BOOL _virtualHEAD;
|
||||
|
||||
CFHTTPMessageRef _requestMessage;
|
||||
GCDWebServerRequest* _request;
|
||||
GCDWebServerHandler* _handler;
|
||||
CFHTTPMessageRef _responseMessage;
|
||||
GCDWebServerResponse* _response;
|
||||
NSInteger _statusCode;
|
||||
|
||||
BOOL _opened;
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
NSUInteger _connectionIndex;
|
||||
NSString* _requestPath;
|
||||
int _requestFD;
|
||||
NSString* _responsePath;
|
||||
int _responseFD;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (void)initialize {
|
||||
if (_CRLFData == nil) {
|
||||
_CRLFData = [[NSData alloc] initWithBytes:"\r\n" length:2];
|
||||
GWS_DCHECK(_CRLFData);
|
||||
}
|
||||
if (_CRLFCRLFData == nil) {
|
||||
_CRLFCRLFData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
|
||||
GWS_DCHECK(_CRLFCRLFData);
|
||||
}
|
||||
if (_continueData == nil) {
|
||||
CFHTTPMessageRef message = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 100, NULL, kCFHTTPVersion1_1);
|
||||
_continueData = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(message));
|
||||
CFRelease(message);
|
||||
GWS_DCHECK(_continueData);
|
||||
}
|
||||
if (_lastChunkData == nil) {
|
||||
_lastChunkData = [[NSData alloc] initWithBytes:"0\r\n\r\n" length:5];
|
||||
}
|
||||
if (_digestAuthenticationNonce == nil) {
|
||||
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
|
||||
_digestAuthenticationNonce = GCDWebServerComputeMD5Digest(@"%@", CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid)));
|
||||
CFRelease(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isUsingIPv6 {
|
||||
const struct sockaddr* localSockAddr = _localAddressData.bytes;
|
||||
return (localSockAddr->sa_family == AF_INET6);
|
||||
}
|
||||
|
||||
- (void)_initializeResponseHeadersWithStatusCode:(NSInteger)statusCode {
|
||||
_statusCode = statusCode;
|
||||
_responseMessage = CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_1);
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Connection"), CFSTR("Close"));
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Server"), (__bridge CFStringRef)_server.serverName);
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Date"), (__bridge CFStringRef)GCDWebServerFormatRFC822([NSDate date]));
|
||||
}
|
||||
|
||||
- (void)_startProcessingRequest {
|
||||
GWS_DCHECK(_responseMessage == NULL);
|
||||
|
||||
GCDWebServerResponse* preflightResponse = [self preflightRequest:_request];
|
||||
if (preflightResponse) {
|
||||
[self _finishProcessingRequest:preflightResponse];
|
||||
} else {
|
||||
[self processRequest:_request
|
||||
completion:^(GCDWebServerResponse* processResponse) {
|
||||
[self _finishProcessingRequest:processResponse];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
- (void)_finishProcessingRequest:(GCDWebServerResponse*)response {
|
||||
GWS_DCHECK(_responseMessage == NULL);
|
||||
BOOL hasBody = NO;
|
||||
|
||||
if (response) {
|
||||
response = [self overrideResponse:response forRequest:_request];
|
||||
}
|
||||
if (response) {
|
||||
if ([response hasBody]) {
|
||||
[response prepareForReading];
|
||||
hasBody = !_virtualHEAD;
|
||||
}
|
||||
NSError* error = nil;
|
||||
if (hasBody && ![response performOpen:&error]) {
|
||||
GWS_LOG_ERROR(@"Failed opening response body for socket %i: %@", _socket, error);
|
||||
} else {
|
||||
_response = response;
|
||||
}
|
||||
}
|
||||
|
||||
if (_response) {
|
||||
[self _initializeResponseHeadersWithStatusCode:_response.statusCode];
|
||||
if (_response.lastModifiedDate) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Last-Modified"), (__bridge CFStringRef)GCDWebServerFormatRFC822((NSDate*)_response.lastModifiedDate));
|
||||
}
|
||||
if (_response.eTag) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("ETag"), (__bridge CFStringRef)_response.eTag);
|
||||
}
|
||||
if ((_response.statusCode >= 200) && (_response.statusCode < 300)) {
|
||||
if (_response.cacheControlMaxAge > 0) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), (__bridge CFStringRef)[NSString stringWithFormat:@"max-age=%i, public", (int)_response.cacheControlMaxAge]);
|
||||
} else {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), CFSTR("no-cache"));
|
||||
}
|
||||
}
|
||||
if (_response.contentType != nil) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Type"), (__bridge CFStringRef)GCDWebServerNormalizeHeaderValue(_response.contentType));
|
||||
}
|
||||
if (_response.contentLength != NSUIntegerMax) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Length"), (__bridge CFStringRef)[NSString stringWithFormat:@"%lu", (unsigned long)_response.contentLength]);
|
||||
}
|
||||
if (_response.usesChunkedTransferEncoding) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Transfer-Encoding"), CFSTR("chunked"));
|
||||
}
|
||||
[_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) {
|
||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
|
||||
}];
|
||||
[self writeHeadersWithCompletionBlock:^(BOOL success) {
|
||||
|
||||
if (success) {
|
||||
if (hasBody) {
|
||||
[self writeBodyWithCompletionBlock:^(BOOL successInner) {
|
||||
|
||||
[_response performClose]; // TODO: There's nothing we can do on failure as headers have already been sent
|
||||
|
||||
}];
|
||||
}
|
||||
} else if (hasBody) {
|
||||
[_response performClose];
|
||||
}
|
||||
|
||||
}];
|
||||
} else {
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_readBodyWithLength:(NSUInteger)length initialData:(NSData*)initialData {
|
||||
NSError* error = nil;
|
||||
if (![_request performOpen:&error]) {
|
||||
GWS_LOG_ERROR(@"Failed opening request body for socket %i: %@", _socket, error);
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
return;
|
||||
}
|
||||
|
||||
if (initialData.length) {
|
||||
if (![_request performWriteData:initialData error:&error]) {
|
||||
GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
|
||||
if (![_request performClose:&error]) {
|
||||
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
||||
}
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
return;
|
||||
}
|
||||
length -= initialData.length;
|
||||
}
|
||||
|
||||
if (length) {
|
||||
[self readBodyWithRemainingLength:length
|
||||
completionBlock:^(BOOL success) {
|
||||
|
||||
NSError* localError = nil;
|
||||
if ([_request performClose:&localError]) {
|
||||
[self _startProcessingRequest];
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
}
|
||||
|
||||
}];
|
||||
} else {
|
||||
if ([_request performClose:&error]) {
|
||||
[self _startProcessingRequest];
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_readChunkedBodyWithInitialData:(NSData*)initialData {
|
||||
NSError* error = nil;
|
||||
if (![_request performOpen:&error]) {
|
||||
GWS_LOG_ERROR(@"Failed opening request body for socket %i: %@", _socket, error);
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableData* chunkData = [[NSMutableData alloc] initWithData:initialData];
|
||||
[self readNextBodyChunk:chunkData
|
||||
completionBlock:^(BOOL success) {
|
||||
|
||||
NSError* localError = nil;
|
||||
if ([_request performClose:&localError]) {
|
||||
[self _startProcessingRequest];
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
}
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_readRequestHeaders {
|
||||
_requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
|
||||
NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity];
|
||||
[self readHeaders:headersData
|
||||
withCompletionBlock:^(NSData* extraData) {
|
||||
|
||||
if (extraData) {
|
||||
NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(_requestMessage)); // Method verbs are case-sensitive and uppercase
|
||||
if (_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) {
|
||||
requestMethod = @"GET";
|
||||
_virtualHEAD = YES;
|
||||
}
|
||||
NSDictionary* requestHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones
|
||||
NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(_requestMessage));
|
||||
if (requestURL) {
|
||||
requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders];
|
||||
GWS_DCHECK(requestURL);
|
||||
}
|
||||
NSString* urlPath = requestURL ? CFBridgingRelease(CFURLCopyPath((CFURLRef)requestURL)) : nil; // Don't use -[NSURL path] which strips the ending slash
|
||||
if (urlPath == nil) {
|
||||
urlPath = @"/"; // CFURLCopyPath() returns NULL for a relative URL with path "//" contrary to -[NSURL path] which returns "/"
|
||||
}
|
||||
NSString* requestPath = urlPath ? GCDWebServerUnescapeURLString(urlPath) : nil;
|
||||
NSString* queryString = requestURL ? CFBridgingRelease(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped;
|
||||
NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{};
|
||||
if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) {
|
||||
for (_handler in _server.handlers) {
|
||||
_request = _handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery);
|
||||
if (_request) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_request) {
|
||||
_request.localAddressData = self.localAddressData;
|
||||
_request.remoteAddressData = self.remoteAddressData;
|
||||
if ([_request hasBody]) {
|
||||
[_request prepareForWriting];
|
||||
if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) {
|
||||
NSString* expectHeader = [requestHeaders objectForKey:@"Expect"];
|
||||
if (expectHeader) {
|
||||
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing
|
||||
[self writeData:_continueData
|
||||
withCompletionBlock:^(BOOL success) {
|
||||
|
||||
if (success) {
|
||||
if (_request.usesChunkedTransferEncoding) {
|
||||
[self _readChunkedBodyWithInitialData:extraData];
|
||||
} else {
|
||||
[self _readBodyWithLength:_request.contentLength initialData:extraData];
|
||||
}
|
||||
}
|
||||
|
||||
}];
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", _socket);
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed];
|
||||
}
|
||||
} else {
|
||||
if (_request.usesChunkedTransferEncoding) {
|
||||
[self _readChunkedBodyWithInitialData:extraData];
|
||||
} else {
|
||||
[self _readBodyWithLength:_request.contentLength initialData:extraData];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", _socket);
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest];
|
||||
}
|
||||
} else {
|
||||
[self _startProcessingRequest];
|
||||
}
|
||||
} else {
|
||||
_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery];
|
||||
GWS_DCHECK(_request);
|
||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_NotImplemented];
|
||||
}
|
||||
} else {
|
||||
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
} else {
|
||||
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||
}
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (instancetype)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket {
|
||||
if ((self = [super init])) {
|
||||
_server = server;
|
||||
_localAddressData = localAddress;
|
||||
_remoteAddressData = remoteAddress;
|
||||
_socket = socket;
|
||||
GWS_LOG_DEBUG(@"Did open connection on socket %i", _socket);
|
||||
|
||||
[_server willStartConnection:self];
|
||||
|
||||
if (![self open]) {
|
||||
close(_socket);
|
||||
return nil;
|
||||
}
|
||||
_opened = YES;
|
||||
|
||||
[self _readRequestHeaders];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString*)localAddressString {
|
||||
return GCDWebServerStringFromSockAddr(_localAddressData.bytes, YES);
|
||||
}
|
||||
|
||||
- (NSString*)remoteAddressString {
|
||||
return GCDWebServerStringFromSockAddr(_remoteAddressData.bytes, YES);
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
int result = close(_socket);
|
||||
if (result != 0) {
|
||||
GWS_LOG_ERROR(@"Failed closing socket %i for connection: %s (%i)", _socket, strerror(errno), errno);
|
||||
} else {
|
||||
GWS_LOG_DEBUG(@"Did close connection on socket %i", _socket);
|
||||
}
|
||||
|
||||
if (_opened) {
|
||||
[self close];
|
||||
}
|
||||
|
||||
[_server didEndConnection:self];
|
||||
|
||||
if (_requestMessage) {
|
||||
CFRelease(_requestMessage);
|
||||
}
|
||||
|
||||
if (_responseMessage) {
|
||||
CFRelease(_responseMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerConnection (Read)
|
||||
|
||||
- (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block {
|
||||
dispatch_read(_socket, length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t buffer, int error) {
|
||||
|
||||
@autoreleasepool {
|
||||
if (error == 0) {
|
||||
size_t size = dispatch_data_get_size(buffer);
|
||||
if (size > 0) {
|
||||
NSUInteger originalLength = data.length;
|
||||
dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
|
||||
[data appendBytes:chunkBytes length:chunkSize];
|
||||
return true;
|
||||
});
|
||||
[self didReadBytes:((char*)data.bytes + originalLength) length:(data.length - originalLength)];
|
||||
block(YES);
|
||||
} else {
|
||||
if (_totalBytesRead > 0) {
|
||||
GWS_LOG_ERROR(@"No more data available on socket %i", _socket);
|
||||
} else {
|
||||
GWS_LOG_WARNING(@"No data received from socket %i", _socket);
|
||||
}
|
||||
block(NO);
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Error while reading from socket %i: %s (%i)", _socket, strerror(error), error);
|
||||
block(NO);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
- (void)readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block {
|
||||
GWS_DCHECK(_requestMessage);
|
||||
[self readData:headersData
|
||||
withLength:NSUIntegerMax
|
||||
completionBlock:^(BOOL success) {
|
||||
|
||||
if (success) {
|
||||
NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)];
|
||||
if (range.location == NSNotFound) {
|
||||
[self readHeaders:headersData withCompletionBlock:block];
|
||||
} else {
|
||||
NSUInteger length = range.location + range.length;
|
||||
if (CFHTTPMessageAppendBytes(_requestMessage, headersData.bytes, length)) {
|
||||
if (CFHTTPMessageIsHeaderComplete(_requestMessage)) {
|
||||
block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]);
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", _socket);
|
||||
block(nil);
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", _socket);
|
||||
block(nil);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
block(nil);
|
||||
}
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block {
|
||||
GWS_DCHECK([_request hasBody] && ![_request usesChunkedTransferEncoding]);
|
||||
NSMutableData* bodyData = [[NSMutableData alloc] initWithCapacity:kBodyReadCapacity];
|
||||
[self readData:bodyData
|
||||
withLength:length
|
||||
completionBlock:^(BOOL success) {
|
||||
|
||||
if (success) {
|
||||
if (bodyData.length <= length) {
|
||||
NSError* error = nil;
|
||||
if ([_request performWriteData:bodyData error:&error]) {
|
||||
NSUInteger remainingLength = length - bodyData.length;
|
||||
if (remainingLength) {
|
||||
[self readBodyWithRemainingLength:remainingLength completionBlock:block];
|
||||
} else {
|
||||
block(YES);
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
|
||||
block(NO);
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", _socket);
|
||||
block(NO);
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
} else {
|
||||
block(NO);
|
||||
}
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
char buffer[size + 1];
|
||||
bcopy(bytes, buffer, size);
|
||||
buffer[size] = 0;
|
||||
char* end = NULL;
|
||||
long result = strtol(buffer, &end, 16);
|
||||
return ((end != NULL) && (*end == 0) && (result >= 0) ? result : NSNotFound);
|
||||
}
|
||||
|
||||
- (void)readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block {
|
||||
GWS_DCHECK([_request hasBody] && [_request usesChunkedTransferEncoding]);
|
||||
|
||||
while (1) {
|
||||
NSRange range = [chunkData rangeOfData:_CRLFData options:0 range:NSMakeRange(0, chunkData.length)];
|
||||
if (range.location == NSNotFound) {
|
||||
break;
|
||||
}
|
||||
NSRange extensionRange = [chunkData rangeOfData:[NSData dataWithBytes:";" length:1] options:0 range:NSMakeRange(0, range.location)]; // Ignore chunk extensions
|
||||
NSUInteger length = _ScanHexNumber((char*)chunkData.bytes, extensionRange.location != NSNotFound ? extensionRange.location : range.location);
|
||||
if (length != NSNotFound) {
|
||||
if (length) {
|
||||
if (chunkData.length < range.location + range.length + length + 2) {
|
||||
break;
|
||||
}
|
||||
const char* ptr = (char*)chunkData.bytes + range.location + range.length + length;
|
||||
if ((*ptr == '\r') && (*(ptr + 1) == '\n')) {
|
||||
NSError* error = nil;
|
||||
if ([_request performWriteData:[chunkData subdataWithRange:NSMakeRange(range.location + range.length, length)] error:&error]) {
|
||||
[chunkData replaceBytesInRange:NSMakeRange(0, range.location + range.length + length + 2) withBytes:NULL length:0];
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
|
||||
block(NO);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Missing terminating CRLF sequence for chunk reading request body on socket %i", _socket);
|
||||
block(NO);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
NSRange trailerRange = [chunkData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(range.location, chunkData.length - range.location)]; // Ignore trailers
|
||||
if (trailerRange.location != NSNotFound) {
|
||||
block(YES);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Invalid chunk length reading request body on socket %i", _socket);
|
||||
block(NO);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[self readData:chunkData
|
||||
withLength:NSUIntegerMax
|
||||
completionBlock:^(BOOL success) {
|
||||
|
||||
if (success) {
|
||||
[self readNextBodyChunk:chunkData completionBlock:block];
|
||||
} else {
|
||||
block(NO);
|
||||
}
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerConnection (Write)
|
||||
|
||||
- (void)writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block {
|
||||
dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^{
|
||||
[data self]; // Keeps ARC from releasing data too early
|
||||
});
|
||||
dispatch_write(_socket, buffer, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t remainingData, int error) {
|
||||
|
||||
@autoreleasepool {
|
||||
if (error == 0) {
|
||||
GWS_DCHECK(remainingData == NULL);
|
||||
[self didWriteBytes:data.bytes length:data.length];
|
||||
block(YES);
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Error while writing to socket %i: %s (%i)", _socket, strerror(error), error);
|
||||
block(NO);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
||||
dispatch_release(buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block {
|
||||
GWS_DCHECK(_responseMessage);
|
||||
CFDataRef data = CFHTTPMessageCopySerializedMessage(_responseMessage);
|
||||
[self writeData:(__bridge NSData*)data withCompletionBlock:block];
|
||||
CFRelease(data);
|
||||
}
|
||||
|
||||
- (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
|
||||
GWS_DCHECK([_response hasBody]);
|
||||
[_response performReadDataWithCompletion:^(NSData* data, NSError* error) {
|
||||
|
||||
if (data) {
|
||||
if (data.length) {
|
||||
if (_response.usesChunkedTransferEncoding) {
|
||||
const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String];
|
||||
size_t hexLength = strlen(hexString);
|
||||
NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)];
|
||||
if (chunk == nil) {
|
||||
GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", _socket, error);
|
||||
block(NO);
|
||||
return;
|
||||
}
|
||||
char* ptr = (char*)[(NSMutableData*)chunk mutableBytes];
|
||||
bcopy(hexString, ptr, hexLength);
|
||||
ptr += hexLength;
|
||||
*ptr++ = '\r';
|
||||
*ptr++ = '\n';
|
||||
bcopy(data.bytes, ptr, data.length);
|
||||
ptr += data.length;
|
||||
*ptr++ = '\r';
|
||||
*ptr = '\n';
|
||||
data = chunk;
|
||||
}
|
||||
[self writeData:data
|
||||
withCompletionBlock:^(BOOL success) {
|
||||
|
||||
if (success) {
|
||||
[self writeBodyWithCompletionBlock:block];
|
||||
} else {
|
||||
block(NO);
|
||||
}
|
||||
|
||||
}];
|
||||
} else {
|
||||
if (_response.usesChunkedTransferEncoding) {
|
||||
[self writeData:_lastChunkData
|
||||
withCompletionBlock:^(BOOL success) {
|
||||
|
||||
block(success);
|
||||
|
||||
}];
|
||||
} else {
|
||||
block(YES);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error);
|
||||
block(NO);
|
||||
}
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerConnection (Subclassing)
|
||||
|
||||
- (BOOL)open {
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
if (_server.recordingEnabled) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
_connectionIndex = OSAtomicIncrement32(&_connectionCounter);
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
_requestPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
_requestFD = open([_requestPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
GWS_DCHECK(_requestFD > 0);
|
||||
|
||||
_responsePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
_responseFD = open([_responsePath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
GWS_DCHECK(_responseFD > 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length {
|
||||
GWS_LOG_DEBUG(@"Connection received %lu bytes on socket %i", (unsigned long)length, _socket);
|
||||
_totalBytesRead += length;
|
||||
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
if ((_requestFD > 0) && (write(_requestFD, bytes, length) != (ssize_t)length)) {
|
||||
GWS_LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno);
|
||||
close(_requestFD);
|
||||
_requestFD = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length {
|
||||
GWS_LOG_DEBUG(@"Connection sent %lu bytes on socket %i", (unsigned long)length, _socket);
|
||||
_totalBytesWritten += length;
|
||||
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
if ((_responseFD > 0) && (write(_responseFD, bytes, length) != (ssize_t)length)) {
|
||||
GWS_LOG_ERROR(@"Failed recording response data: %s (%i)", strerror(errno), errno);
|
||||
close(_responseFD);
|
||||
_responseFD = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary*)headers {
|
||||
return url;
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc2617
|
||||
- (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request {
|
||||
GWS_LOG_DEBUG(@"Connection on socket %i preflighting request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead);
|
||||
GCDWebServerResponse* response = nil;
|
||||
if (_server.authenticationBasicAccounts) {
|
||||
__block BOOL authenticated = NO;
|
||||
NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"];
|
||||
if ([authorizationHeader hasPrefix:@"Basic "]) {
|
||||
NSString* basicAccount = [authorizationHeader substringFromIndex:6];
|
||||
[_server.authenticationBasicAccounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* digest, BOOL* stop) {
|
||||
if ([basicAccount isEqualToString:digest]) {
|
||||
authenticated = YES;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
}
|
||||
if (!authenticated) {
|
||||
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized];
|
||||
[response setValue:[NSString stringWithFormat:@"Basic realm=\"%@\"", _server.authenticationRealm] forAdditionalHeader:@"WWW-Authenticate"];
|
||||
}
|
||||
} else if (_server.authenticationDigestAccounts) {
|
||||
BOOL authenticated = NO;
|
||||
BOOL isStaled = NO;
|
||||
NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"];
|
||||
if ([authorizationHeader hasPrefix:@"Digest "]) {
|
||||
NSString* realm = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"realm");
|
||||
if (realm && [_server.authenticationRealm isEqualToString:realm]) {
|
||||
NSString* nonce = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"nonce");
|
||||
if ([nonce isEqualToString:_digestAuthenticationNonce]) {
|
||||
NSString* username = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"username");
|
||||
NSString* uri = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"uri");
|
||||
NSString* actualResponse = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"response");
|
||||
NSString* ha1 = [_server.authenticationDigestAccounts objectForKey:username];
|
||||
NSString* ha2 = GCDWebServerComputeMD5Digest(@"%@:%@", request.method, uri); // We cannot use "request.path" as the query string is required
|
||||
NSString* expectedResponse = GCDWebServerComputeMD5Digest(@"%@:%@:%@", ha1, _digestAuthenticationNonce, ha2);
|
||||
if ([actualResponse isEqualToString:expectedResponse]) {
|
||||
authenticated = YES;
|
||||
}
|
||||
} else if (nonce.length) {
|
||||
isStaled = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!authenticated) {
|
||||
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized];
|
||||
[response setValue:[NSString stringWithFormat:@"Digest realm=\"%@\", nonce=\"%@\"%@", _server.authenticationRealm, _digestAuthenticationNonce, isStaled ? @", stale=TRUE" : @""] forAdditionalHeader:@"WWW-Authenticate"]; // TODO: Support Quality of Protection ("qop")
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion {
|
||||
GWS_LOG_DEBUG(@"Connection on socket %i processing request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead);
|
||||
_handler.asyncProcessBlock(request, [completion copy]);
|
||||
}
|
||||
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
|
||||
static inline BOOL _CompareResources(NSString* responseETag, NSString* requestETag, NSDate* responseLastModified, NSDate* requestLastModified) {
|
||||
if (requestLastModified && responseLastModified) {
|
||||
if ([responseLastModified compare:requestLastModified] != NSOrderedDescending) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
if (requestETag && responseETag) { // Per the specs "If-None-Match" must be checked after "If-Modified-Since"
|
||||
if ([requestETag isEqualToString:@"*"]) {
|
||||
return YES;
|
||||
}
|
||||
if ([responseETag isEqualToString:requestETag]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request {
|
||||
if ((response.statusCode >= 200) && (response.statusCode < 300) && _CompareResources(response.eTag, request.ifNoneMatch, response.lastModifiedDate, request.ifModifiedSince)) {
|
||||
NSInteger code = [request.method isEqualToString:@"HEAD"] || [request.method isEqualToString:@"GET"] ? kGCDWebServerHTTPStatusCode_NotModified : kGCDWebServerHTTPStatusCode_PreconditionFailed;
|
||||
GCDWebServerResponse* newResponse = [GCDWebServerResponse responseWithStatusCode:code];
|
||||
newResponse.cacheControlMaxAge = response.cacheControlMaxAge;
|
||||
newResponse.lastModifiedDate = response.lastModifiedDate;
|
||||
newResponse.eTag = response.eTag;
|
||||
GWS_DCHECK(newResponse);
|
||||
return newResponse;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
- (void)abortRequest:(GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode {
|
||||
GWS_DCHECK(_responseMessage == NULL);
|
||||
GWS_DCHECK((statusCode >= 400) && (statusCode < 600));
|
||||
[self _initializeResponseHeadersWithStatusCode:statusCode];
|
||||
[self writeHeadersWithCompletionBlock:^(BOOL success) {
|
||||
; // Nothing more to do
|
||||
}];
|
||||
GWS_LOG_DEBUG(@"Connection aborted with status code %i on socket %i", (int)statusCode, _socket);
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
if (_requestPath) {
|
||||
BOOL success = NO;
|
||||
NSError* error = nil;
|
||||
if (_requestFD > 0) {
|
||||
close(_requestFD);
|
||||
NSString* name = [NSString stringWithFormat:@"%03lu-%@.request", (unsigned long)_connectionIndex, _virtualHEAD ? @"HEAD" : _request.method];
|
||||
success = [[NSFileManager defaultManager] moveItemAtPath:_requestPath toPath:[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:name] error:&error];
|
||||
}
|
||||
if (!success) {
|
||||
GWS_LOG_ERROR(@"Failed saving recorded request: %@", error);
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
unlink([_requestPath fileSystemRepresentation]);
|
||||
}
|
||||
|
||||
if (_responsePath) {
|
||||
BOOL success = NO;
|
||||
NSError* error = nil;
|
||||
if (_responseFD > 0) {
|
||||
close(_responseFD);
|
||||
NSString* name = [NSString stringWithFormat:@"%03lu-%i.response", (unsigned long)_connectionIndex, (int)_statusCode];
|
||||
success = [[NSFileManager defaultManager] moveItemAtPath:_responsePath toPath:[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:name] error:&error];
|
||||
}
|
||||
if (!success) {
|
||||
GWS_LOG_ERROR(@"Failed saving recorded response: %@", error);
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
unlink([_responsePath fileSystemRepresentation]);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_request) {
|
||||
GWS_LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_totalBytesRead, (unsigned long)_totalBytesWritten);
|
||||
} else {
|
||||
GWS_LOG_VERBOSE(@"[%@] %@ %i \"(invalid request)\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, (unsigned long)_totalBytesRead, (unsigned long)_totalBytesWritten);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Converts a file extension to the corresponding MIME type.
|
||||
* If there is no match, "application/octet-stream" is returned.
|
||||
*
|
||||
* Overrides allow to customize the built-in mapping from extensions to MIME
|
||||
* types. Keys of the dictionary must be lowercased file extensions without
|
||||
* the period, and the values must be the corresponding MIME types.
|
||||
*/
|
||||
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary* _Nullable overrides);
|
||||
|
||||
/**
|
||||
* Add percent-escapes to a string so it can be used in a URL.
|
||||
* The legal characters ":@/?&=+" are also escaped to ensure compatibility
|
||||
* with URL encoded forms and URL queries.
|
||||
*/
|
||||
NSString* _Nullable GCDWebServerEscapeURLString(NSString* string);
|
||||
|
||||
/**
|
||||
* Unescapes a URL percent-encoded string.
|
||||
*/
|
||||
NSString* _Nullable GCDWebServerUnescapeURLString(NSString* string);
|
||||
|
||||
/**
|
||||
* Extracts the unescaped names and values from an
|
||||
* "application/x-www-form-urlencoded" form.
|
||||
* http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
|
||||
*/
|
||||
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form);
|
||||
|
||||
/**
|
||||
* On OS X, returns the IPv4 or IPv6 address as a string of the primary
|
||||
* connected service or nil if not available.
|
||||
*
|
||||
* On iOS, returns the IPv4 or IPv6 address as a string of the WiFi
|
||||
* interface if connected or nil otherwise.
|
||||
*/
|
||||
NSString* _Nullable GCDWebServerGetPrimaryIPAddress(BOOL useIPv6);
|
||||
|
||||
/**
|
||||
* Converts a date into a string using RFC822 formatting.
|
||||
* https://tools.ietf.org/html/rfc822#section-5
|
||||
* https://tools.ietf.org/html/rfc1123#section-5.2.14
|
||||
*/
|
||||
NSString* GCDWebServerFormatRFC822(NSDate* date);
|
||||
|
||||
/**
|
||||
* Converts a RFC822 formatted string into a date.
|
||||
* https://tools.ietf.org/html/rfc822#section-5
|
||||
* https://tools.ietf.org/html/rfc1123#section-5.2.14
|
||||
*
|
||||
* @warning Timezones other than GMT are not supported by this function.
|
||||
*/
|
||||
NSDate* _Nullable GCDWebServerParseRFC822(NSString* string);
|
||||
|
||||
/**
|
||||
* Converts a date into a string using IOS 8601 formatting.
|
||||
* http://tools.ietf.org/html/rfc3339#section-5.6
|
||||
*/
|
||||
NSString* GCDWebServerFormatISO8601(NSDate* date);
|
||||
|
||||
/**
|
||||
* Converts a ISO 8601 formatted string into a date.
|
||||
* http://tools.ietf.org/html/rfc3339#section-5.6
|
||||
*
|
||||
* @warning Only "calendar" variant is supported at this time and timezones
|
||||
* other than GMT are not supported either.
|
||||
*/
|
||||
NSDate* _Nullable GCDWebServerParseISO8601(NSString* string);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
#else
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#endif
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
#import <ifaddrs.h>
|
||||
#import <net/if.h>
|
||||
#import <netdb.h>
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
static NSDateFormatter* _dateFormatterRFC822 = nil;
|
||||
static NSDateFormatter* _dateFormatterISO8601 = nil;
|
||||
static dispatch_queue_t _dateFormatterQueue = NULL;
|
||||
|
||||
// TODO: Handle RFC 850 and ANSI C's asctime() format
|
||||
void GCDWebServerInitializeFunctions() {
|
||||
GWS_DCHECK([NSThread isMainThread]); // NSDateFormatter should be initialized on main thread
|
||||
if (_dateFormatterRFC822 == nil) {
|
||||
_dateFormatterRFC822 = [[NSDateFormatter alloc] init];
|
||||
_dateFormatterRFC822.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
|
||||
_dateFormatterRFC822.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
|
||||
_dateFormatterRFC822.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
|
||||
GWS_DCHECK(_dateFormatterRFC822);
|
||||
}
|
||||
if (_dateFormatterISO8601 == nil) {
|
||||
_dateFormatterISO8601 = [[NSDateFormatter alloc] init];
|
||||
_dateFormatterISO8601.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
|
||||
_dateFormatterISO8601.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'+00:00'";
|
||||
_dateFormatterISO8601.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
|
||||
GWS_DCHECK(_dateFormatterISO8601);
|
||||
}
|
||||
if (_dateFormatterQueue == NULL) {
|
||||
_dateFormatterQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
|
||||
GWS_DCHECK(_dateFormatterQueue);
|
||||
}
|
||||
}
|
||||
|
||||
NSString* GCDWebServerNormalizeHeaderValue(NSString* value) {
|
||||
if (value) {
|
||||
NSRange range = [value rangeOfString:@";"]; // Assume part before ";" separator is case-insensitive
|
||||
if (range.location != NSNotFound) {
|
||||
value = [[[value substringToIndex:range.location] lowercaseString] stringByAppendingString:[value substringFromIndex:range.location]];
|
||||
} else {
|
||||
value = [value lowercaseString];
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
NSString* GCDWebServerTruncateHeaderValue(NSString* value) {
|
||||
if (value) {
|
||||
NSRange range = [value rangeOfString:@";"];
|
||||
if (range.location != NSNotFound) {
|
||||
return [value substringToIndex:range.location];
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
NSString* GCDWebServerExtractHeaderValueParameter(NSString* value, NSString* name) {
|
||||
NSString* parameter = nil;
|
||||
if (value) {
|
||||
NSScanner* scanner = [[NSScanner alloc] initWithString:value];
|
||||
[scanner setCaseSensitive:NO]; // Assume parameter names are case-insensitive
|
||||
NSString* string = [NSString stringWithFormat:@"%@=", name];
|
||||
if ([scanner scanUpToString:string intoString:NULL]) {
|
||||
[scanner scanString:string intoString:NULL];
|
||||
if ([scanner scanString:@"\"" intoString:NULL]) {
|
||||
[scanner scanUpToString:@"\"" intoString:¶meter];
|
||||
} else {
|
||||
[scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:¶meter];
|
||||
}
|
||||
}
|
||||
}
|
||||
return parameter;
|
||||
}
|
||||
|
||||
// http://www.w3schools.com/tags/ref_charactersets.asp
|
||||
NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset) {
|
||||
NSStringEncoding encoding = kCFStringEncodingInvalidId;
|
||||
if (charset) {
|
||||
encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)charset));
|
||||
}
|
||||
return (encoding != kCFStringEncodingInvalidId ? encoding : NSUTF8StringEncoding);
|
||||
}
|
||||
|
||||
NSString* GCDWebServerFormatRFC822(NSDate* date) {
|
||||
__block NSString* string;
|
||||
dispatch_sync(_dateFormatterQueue, ^{
|
||||
string = [_dateFormatterRFC822 stringFromDate:date];
|
||||
});
|
||||
return string;
|
||||
}
|
||||
|
||||
NSDate* GCDWebServerParseRFC822(NSString* string) {
|
||||
__block NSDate* date;
|
||||
dispatch_sync(_dateFormatterQueue, ^{
|
||||
date = [_dateFormatterRFC822 dateFromString:string];
|
||||
});
|
||||
return date;
|
||||
}
|
||||
|
||||
NSString* GCDWebServerFormatISO8601(NSDate* date) {
|
||||
__block NSString* string;
|
||||
dispatch_sync(_dateFormatterQueue, ^{
|
||||
string = [_dateFormatterISO8601 stringFromDate:date];
|
||||
});
|
||||
return string;
|
||||
}
|
||||
|
||||
NSDate* GCDWebServerParseISO8601(NSString* string) {
|
||||
__block NSDate* date;
|
||||
dispatch_sync(_dateFormatterQueue, ^{
|
||||
date = [_dateFormatterISO8601 dateFromString:string];
|
||||
});
|
||||
return date;
|
||||
}
|
||||
|
||||
BOOL GCDWebServerIsTextContentType(NSString* type) {
|
||||
return ([type hasPrefix:@"text/"] || [type hasPrefix:@"application/json"] || [type hasPrefix:@"application/xml"]);
|
||||
}
|
||||
|
||||
NSString* GCDWebServerDescribeData(NSData* data, NSString* type) {
|
||||
if (GCDWebServerIsTextContentType(type)) {
|
||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(type, @"charset");
|
||||
NSString* string = [[NSString alloc] initWithData:data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||
if (string) {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
return [NSString stringWithFormat:@"<%lu bytes>", (unsigned long)data.length];
|
||||
}
|
||||
|
||||
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary* overrides) {
|
||||
NSDictionary* builtInOverrides = @{ @"css" : @"text/css" };
|
||||
NSString* mimeType = nil;
|
||||
extension = [extension lowercaseString];
|
||||
if (extension.length) {
|
||||
mimeType = [overrides objectForKey:extension];
|
||||
if (mimeType == nil) {
|
||||
mimeType = [builtInOverrides objectForKey:extension];
|
||||
}
|
||||
if (mimeType == nil) {
|
||||
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
|
||||
if (uti) {
|
||||
mimeType = CFBridgingRelease(UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType));
|
||||
CFRelease(uti);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mimeType ? mimeType : kGCDWebServerDefaultMimeType;
|
||||
}
|
||||
|
||||
NSString* GCDWebServerEscapeURLString(NSString* string) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":@/?&=+"), kCFStringEncodingUTF8));
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
NSString* GCDWebServerUnescapeURLString(NSString* string) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)string, CFSTR(""), kCFStringEncodingUTF8));
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
|
||||
NSMutableDictionary* parameters = [NSMutableDictionary dictionary];
|
||||
NSScanner* scanner = [[NSScanner alloc] initWithString:form];
|
||||
[scanner setCharactersToBeSkipped:nil];
|
||||
while (1) {
|
||||
NSString* key = nil;
|
||||
if (![scanner scanUpToString:@"=" intoString:&key] || [scanner isAtEnd]) {
|
||||
break;
|
||||
}
|
||||
[scanner setScanLocation:([scanner scanLocation] + 1)];
|
||||
|
||||
NSString* value = nil;
|
||||
[scanner scanUpToString:@"&" intoString:&value];
|
||||
if (value == nil) {
|
||||
value = @"";
|
||||
}
|
||||
|
||||
key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
||||
NSString* unescapedKey = key ? GCDWebServerUnescapeURLString(key) : nil;
|
||||
value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
||||
NSString* unescapedValue = value ? GCDWebServerUnescapeURLString(value) : nil;
|
||||
if (unescapedKey && unescapedValue) {
|
||||
[parameters setObject:unescapedValue forKey:unescapedKey];
|
||||
} else {
|
||||
GWS_LOG_WARNING(@"Failed parsing URL encoded form for key \"%@\" and value \"%@\"", key, value);
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
|
||||
if ([scanner isAtEnd]) {
|
||||
break;
|
||||
}
|
||||
[scanner setScanLocation:([scanner scanLocation] + 1)];
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService) {
|
||||
char hostBuffer[NI_MAXHOST];
|
||||
char serviceBuffer[NI_MAXSERV];
|
||||
if (getnameinfo(addr, addr->sa_len, hostBuffer, sizeof(hostBuffer), serviceBuffer, sizeof(serviceBuffer), NI_NUMERICHOST | NI_NUMERICSERV | NI_NOFQDN) != 0) {
|
||||
#if DEBUG
|
||||
GWS_DNOT_REACHED();
|
||||
#else
|
||||
return @"";
|
||||
#endif
|
||||
}
|
||||
return includeService ? [NSString stringWithFormat:@"%s:%s", hostBuffer, serviceBuffer] : (NSString*)[NSString stringWithUTF8String:hostBuffer];
|
||||
}
|
||||
|
||||
NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) {
|
||||
NSString* address = nil;
|
||||
#if TARGET_OS_IPHONE
|
||||
#if !TARGET_IPHONE_SIMULATOR && !TARGET_OS_TV
|
||||
const char* primaryInterface = "en0"; // WiFi interface on iOS
|
||||
#endif
|
||||
#else
|
||||
const char* primaryInterface = NULL;
|
||||
SCDynamicStoreRef store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("GCDWebServer"), NULL, NULL);
|
||||
if (store) {
|
||||
CFPropertyListRef info = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4")); // There is no equivalent for IPv6 but the primary interface should be the same
|
||||
if (info) {
|
||||
NSString* interface = [(__bridge NSDictionary*)info objectForKey:@"PrimaryInterface"];
|
||||
if (interface) {
|
||||
primaryInterface = [[NSString stringWithString:interface] UTF8String]; // Copy string to auto-release pool
|
||||
}
|
||||
CFRelease(info);
|
||||
}
|
||||
CFRelease(store);
|
||||
}
|
||||
if (primaryInterface == NULL) {
|
||||
primaryInterface = "lo0";
|
||||
}
|
||||
#endif
|
||||
struct ifaddrs* list;
|
||||
if (getifaddrs(&list) >= 0) {
|
||||
for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) {
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_TV
|
||||
// Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator
|
||||
// Assumption holds for Apple TV running tvOS
|
||||
if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1"))
|
||||
#else
|
||||
if (strcmp(ifap->ifa_name, primaryInterface))
|
||||
#endif
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ((ifap->ifa_flags & IFF_UP) && ((!useIPv6 && (ifap->ifa_addr->sa_family == AF_INET)) || (useIPv6 && (ifap->ifa_addr->sa_family == AF_INET6)))) {
|
||||
address = GCDWebServerStringFromSockAddr(ifap->ifa_addr, NO);
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeifaddrs(list);
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
const char* string = [[[NSString alloc] initWithFormat:format arguments:arguments] UTF8String];
|
||||
va_end(arguments);
|
||||
unsigned char md5[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5(string, (CC_LONG)strlen(string), md5);
|
||||
char buffer[2 * CC_MD5_DIGEST_LENGTH + 1];
|
||||
for (int i = 0; i < CC_MD5_DIGEST_LENGTH; ++i) {
|
||||
unsigned char byte = md5[i];
|
||||
unsigned char byteHi = (byte & 0xF0) >> 4;
|
||||
buffer[2 * i + 0] = byteHi >= 10 ? 'a' + byteHi - 10 : '0' + byteHi;
|
||||
unsigned char byteLo = byte & 0x0F;
|
||||
buffer[2 * i + 1] = byteLo >= 10 ? 'a' + byteLo - 10 : '0' + byteLo;
|
||||
}
|
||||
buffer[2 * CC_MD5_DIGEST_LENGTH] = 0;
|
||||
return (NSString*)[NSString stringWithUTF8String:buffer];
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
// http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* Convenience constants for "informational" HTTP status codes.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, GCDWebServerInformationalHTTPStatusCode) {
|
||||
kGCDWebServerHTTPStatusCode_Continue = 100,
|
||||
kGCDWebServerHTTPStatusCode_SwitchingProtocols = 101,
|
||||
kGCDWebServerHTTPStatusCode_Processing = 102
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience constants for "successful" HTTP status codes.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, GCDWebServerSuccessfulHTTPStatusCode) {
|
||||
kGCDWebServerHTTPStatusCode_OK = 200,
|
||||
kGCDWebServerHTTPStatusCode_Created = 201,
|
||||
kGCDWebServerHTTPStatusCode_Accepted = 202,
|
||||
kGCDWebServerHTTPStatusCode_NonAuthoritativeInformation = 203,
|
||||
kGCDWebServerHTTPStatusCode_NoContent = 204,
|
||||
kGCDWebServerHTTPStatusCode_ResetContent = 205,
|
||||
kGCDWebServerHTTPStatusCode_PartialContent = 206,
|
||||
kGCDWebServerHTTPStatusCode_MultiStatus = 207,
|
||||
kGCDWebServerHTTPStatusCode_AlreadyReported = 208
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience constants for "redirection" HTTP status codes.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, GCDWebServerRedirectionHTTPStatusCode) {
|
||||
kGCDWebServerHTTPStatusCode_MultipleChoices = 300,
|
||||
kGCDWebServerHTTPStatusCode_MovedPermanently = 301,
|
||||
kGCDWebServerHTTPStatusCode_Found = 302,
|
||||
kGCDWebServerHTTPStatusCode_SeeOther = 303,
|
||||
kGCDWebServerHTTPStatusCode_NotModified = 304,
|
||||
kGCDWebServerHTTPStatusCode_UseProxy = 305,
|
||||
kGCDWebServerHTTPStatusCode_TemporaryRedirect = 307,
|
||||
kGCDWebServerHTTPStatusCode_PermanentRedirect = 308
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience constants for "client error" HTTP status codes.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, GCDWebServerClientErrorHTTPStatusCode) {
|
||||
kGCDWebServerHTTPStatusCode_BadRequest = 400,
|
||||
kGCDWebServerHTTPStatusCode_Unauthorized = 401,
|
||||
kGCDWebServerHTTPStatusCode_PaymentRequired = 402,
|
||||
kGCDWebServerHTTPStatusCode_Forbidden = 403,
|
||||
kGCDWebServerHTTPStatusCode_NotFound = 404,
|
||||
kGCDWebServerHTTPStatusCode_MethodNotAllowed = 405,
|
||||
kGCDWebServerHTTPStatusCode_NotAcceptable = 406,
|
||||
kGCDWebServerHTTPStatusCode_ProxyAuthenticationRequired = 407,
|
||||
kGCDWebServerHTTPStatusCode_RequestTimeout = 408,
|
||||
kGCDWebServerHTTPStatusCode_Conflict = 409,
|
||||
kGCDWebServerHTTPStatusCode_Gone = 410,
|
||||
kGCDWebServerHTTPStatusCode_LengthRequired = 411,
|
||||
kGCDWebServerHTTPStatusCode_PreconditionFailed = 412,
|
||||
kGCDWebServerHTTPStatusCode_RequestEntityTooLarge = 413,
|
||||
kGCDWebServerHTTPStatusCode_RequestURITooLong = 414,
|
||||
kGCDWebServerHTTPStatusCode_UnsupportedMediaType = 415,
|
||||
kGCDWebServerHTTPStatusCode_RequestedRangeNotSatisfiable = 416,
|
||||
kGCDWebServerHTTPStatusCode_ExpectationFailed = 417,
|
||||
kGCDWebServerHTTPStatusCode_UnprocessableEntity = 422,
|
||||
kGCDWebServerHTTPStatusCode_Locked = 423,
|
||||
kGCDWebServerHTTPStatusCode_FailedDependency = 424,
|
||||
kGCDWebServerHTTPStatusCode_UpgradeRequired = 426,
|
||||
kGCDWebServerHTTPStatusCode_PreconditionRequired = 428,
|
||||
kGCDWebServerHTTPStatusCode_TooManyRequests = 429,
|
||||
kGCDWebServerHTTPStatusCode_RequestHeaderFieldsTooLarge = 431
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience constants for "server error" HTTP status codes.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, GCDWebServerServerErrorHTTPStatusCode) {
|
||||
kGCDWebServerHTTPStatusCode_InternalServerError = 500,
|
||||
kGCDWebServerHTTPStatusCode_NotImplemented = 501,
|
||||
kGCDWebServerHTTPStatusCode_BadGateway = 502,
|
||||
kGCDWebServerHTTPStatusCode_ServiceUnavailable = 503,
|
||||
kGCDWebServerHTTPStatusCode_GatewayTimeout = 504,
|
||||
kGCDWebServerHTTPStatusCode_HTTPVersionNotSupported = 505,
|
||||
kGCDWebServerHTTPStatusCode_InsufficientStorage = 507,
|
||||
kGCDWebServerHTTPStatusCode_LoopDetected = 508,
|
||||
kGCDWebServerHTTPStatusCode_NotExtended = 510,
|
||||
kGCDWebServerHTTPStatusCode_NetworkAuthenticationRequired = 511
|
||||
};
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <os/object.h>
|
||||
#import <sys/socket.h>
|
||||
|
||||
/**
|
||||
* All GCDWebServer headers.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerHTTPStatusCodes.h"
|
||||
#import "GCDWebServerFunctions.h"
|
||||
|
||||
#import "GCDWebServer.h"
|
||||
#import "GCDWebServerConnection.h"
|
||||
|
||||
#import "GCDWebServerDataRequest.h"
|
||||
#import "GCDWebServerFileRequest.h"
|
||||
#import "GCDWebServerMultiPartFormRequest.h"
|
||||
#import "GCDWebServerURLEncodedFormRequest.h"
|
||||
|
||||
#import "GCDWebServerDataResponse.h"
|
||||
#import "GCDWebServerErrorResponse.h"
|
||||
#import "GCDWebServerFileResponse.h"
|
||||
#import "GCDWebServerStreamedResponse.h"
|
||||
|
||||
/**
|
||||
* Check if a custom logging facility should be used instead.
|
||||
*/
|
||||
|
||||
#if defined(__GCDWEBSERVER_LOGGING_HEADER__)
|
||||
|
||||
#define __GCDWEBSERVER_LOGGING_FACILITY_CUSTOM__
|
||||
|
||||
#import __GCDWEBSERVER_LOGGING_HEADER__
|
||||
|
||||
/**
|
||||
* Automatically detect if XLFacility is available and if so use it as a
|
||||
* logging facility.
|
||||
*/
|
||||
|
||||
#elif defined(__has_include) && __has_include("XLFacilityMacros.h")
|
||||
|
||||
#define __GCDWEBSERVER_LOGGING_FACILITY_XLFACILITY__
|
||||
|
||||
#undef XLOG_TAG
|
||||
#define XLOG_TAG @"gcdwebserver.internal"
|
||||
|
||||
#import "XLFacilityMacros.h"
|
||||
|
||||
#define GWS_LOG_DEBUG(...) XLOG_DEBUG(__VA_ARGS__)
|
||||
#define GWS_LOG_VERBOSE(...) XLOG_VERBOSE(__VA_ARGS__)
|
||||
#define GWS_LOG_INFO(...) XLOG_INFO(__VA_ARGS__)
|
||||
#define GWS_LOG_WARNING(...) XLOG_WARNING(__VA_ARGS__)
|
||||
#define GWS_LOG_ERROR(...) XLOG_ERROR(__VA_ARGS__)
|
||||
|
||||
#define GWS_DCHECK(__CONDITION__) XLOG_DEBUG_CHECK(__CONDITION__)
|
||||
#define GWS_DNOT_REACHED() XLOG_DEBUG_UNREACHABLE()
|
||||
|
||||
/**
|
||||
* If all of the above fail, then use GCDWebServer built-in
|
||||
* logging facility.
|
||||
*/
|
||||
|
||||
#else
|
||||
|
||||
#define __GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__
|
||||
|
||||
typedef NS_ENUM(int, GCDWebServerLoggingLevel) {
|
||||
kGCDWebServerLoggingLevel_Debug = 0,
|
||||
kGCDWebServerLoggingLevel_Verbose,
|
||||
kGCDWebServerLoggingLevel_Info,
|
||||
kGCDWebServerLoggingLevel_Warning,
|
||||
kGCDWebServerLoggingLevel_Error
|
||||
};
|
||||
|
||||
extern GCDWebServerLoggingLevel GCDWebServerLogLevel;
|
||||
extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* _Nonnull format, ...) NS_FORMAT_FUNCTION(2, 3);
|
||||
|
||||
#if DEBUG
|
||||
#define GWS_LOG_DEBUG(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Debug) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Debug, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define GWS_LOG_DEBUG(...)
|
||||
#endif
|
||||
#define GWS_LOG_VERBOSE(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Verbose) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Verbose, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define GWS_LOG_INFO(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Info) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Info, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define GWS_LOG_WARNING(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Warning) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Warning, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define GWS_LOG_ERROR(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Error) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Error, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Consistency check macros used when building Debug only.
|
||||
*/
|
||||
|
||||
#if !defined(GWS_DCHECK) || !defined(GWS_DNOT_REACHED)
|
||||
|
||||
#if DEBUG
|
||||
|
||||
#define GWS_DCHECK(__CONDITION__) \
|
||||
do { \
|
||||
if (!(__CONDITION__)) { \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
#define GWS_DNOT_REACHED() abort()
|
||||
|
||||
#else
|
||||
|
||||
#define GWS_DCHECK(__CONDITION__)
|
||||
#define GWS_DNOT_REACHED()
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* GCDWebServer internal constants and APIs.
|
||||
*/
|
||||
|
||||
#define kGCDWebServerDefaultMimeType @"application/octet-stream"
|
||||
#define kGCDWebServerErrorDomain @"GCDWebServerErrorDomain"
|
||||
|
||||
static inline BOOL GCDWebServerIsValidByteRange(NSRange range) {
|
||||
return ((range.location != NSUIntegerMax) || (range.length > 0));
|
||||
}
|
||||
|
||||
static inline NSError* GCDWebServerMakePosixError(int code) {
|
||||
return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : (NSString*)[NSString stringWithUTF8String:strerror(code)]}];
|
||||
}
|
||||
|
||||
extern void GCDWebServerInitializeFunctions();
|
||||
extern NSString* _Nullable GCDWebServerNormalizeHeaderValue(NSString* _Nullable value);
|
||||
extern NSString* _Nullable GCDWebServerTruncateHeaderValue(NSString* _Nullable value);
|
||||
extern NSString* _Nullable GCDWebServerExtractHeaderValueParameter(NSString* _Nullable value, NSString* attribute);
|
||||
extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset);
|
||||
extern BOOL GCDWebServerIsTextContentType(NSString* type);
|
||||
extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType);
|
||||
extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2);
|
||||
extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService);
|
||||
|
||||
@interface GCDWebServerConnection ()
|
||||
- (instancetype)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket;
|
||||
@end
|
||||
|
||||
@interface GCDWebServer ()
|
||||
@property(nonatomic, readonly) NSMutableArray* handlers;
|
||||
@property(nonatomic, readonly, nullable) NSString* serverName;
|
||||
@property(nonatomic, readonly, nullable) NSString* authenticationRealm;
|
||||
@property(nonatomic, readonly, nullable) NSMutableDictionary* authenticationBasicAccounts;
|
||||
@property(nonatomic, readonly, nullable) NSMutableDictionary* authenticationDigestAccounts;
|
||||
@property(nonatomic, readonly) BOOL shouldAutomaticallyMapHEADToGET;
|
||||
@property(nonatomic, readonly) dispatch_queue_priority_t dispatchQueuePriority;
|
||||
- (void)willStartConnection:(GCDWebServerConnection*)connection;
|
||||
- (void)didEndConnection:(GCDWebServerConnection*)connection;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerHandler : NSObject
|
||||
@property(nonatomic, readonly) GCDWebServerMatchBlock matchBlock;
|
||||
@property(nonatomic, readonly) GCDWebServerAsyncProcessBlock asyncProcessBlock;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerRequest ()
|
||||
@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding;
|
||||
@property(nonatomic) NSData* localAddressData;
|
||||
@property(nonatomic) NSData* remoteAddressData;
|
||||
- (void)prepareForWriting;
|
||||
- (BOOL)performOpen:(NSError**)error;
|
||||
- (BOOL)performWriteData:(NSData*)data error:(NSError**)error;
|
||||
- (BOOL)performClose:(NSError**)error;
|
||||
- (void)setAttribute:(nullable id)attribute forKey:(NSString*)key;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerResponse ()
|
||||
@property(nonatomic, readonly) NSDictionary* additionalHeaders;
|
||||
@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding;
|
||||
- (void)prepareForReading;
|
||||
- (BOOL)performOpen:(NSError**)error;
|
||||
- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block;
|
||||
- (void)performClose;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* Attribute key to retrieve an NSArray containing NSStrings from a GCDWebServerRequest
|
||||
* with the contents of any regular expression captures done on the request path.
|
||||
*
|
||||
* @warning This attribute will only be set on the request if adding a handler using
|
||||
* -addHandlerForMethod:pathRegex:requestClass:processBlock:.
|
||||
*/
|
||||
extern NSString* const GCDWebServerRequestAttribute_RegexCaptures;
|
||||
|
||||
/**
|
||||
* This protocol is used by the GCDWebServerConnection to communicate with
|
||||
* the GCDWebServerRequest and write the received HTTP body data.
|
||||
*
|
||||
* Note that multiple GCDWebServerBodyWriter objects can be chained together
|
||||
* internally e.g. to automatically decode gzip encoded content before
|
||||
* passing it on to the GCDWebServerRequest.
|
||||
*
|
||||
* @warning These methods can be called on any GCD thread.
|
||||
*/
|
||||
@protocol GCDWebServerBodyWriter <NSObject>
|
||||
|
||||
/**
|
||||
* This method is called before any body data is received.
|
||||
*
|
||||
* It should return YES on success or NO on failure and set the "error" argument
|
||||
* which is guaranteed to be non-NULL.
|
||||
*/
|
||||
- (BOOL)open:(NSError**)error;
|
||||
|
||||
/**
|
||||
* This method is called whenever body data has been received.
|
||||
*
|
||||
* It should return YES on success or NO on failure and set the "error" argument
|
||||
* which is guaranteed to be non-NULL.
|
||||
*/
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error;
|
||||
|
||||
/**
|
||||
* This method is called after all body data has been received.
|
||||
*
|
||||
* It should return YES on success or NO on failure and set the "error" argument
|
||||
* which is guaranteed to be non-NULL.
|
||||
*/
|
||||
- (BOOL)close:(NSError**)error;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The GCDWebServerRequest class is instantiated by the GCDWebServerConnection
|
||||
* after the HTTP headers have been received. Each instance wraps a single HTTP
|
||||
* request. If a body is present, the methods from the GCDWebServerBodyWriter
|
||||
* protocol will be called by the GCDWebServerConnection to receive it.
|
||||
*
|
||||
* The default implementation of the GCDWebServerBodyWriter protocol on the class
|
||||
* simply ignores the body data.
|
||||
*
|
||||
* @warning GCDWebServerRequest instances can be created and used on any GCD thread.
|
||||
*/
|
||||
@interface GCDWebServerRequest : NSObject <GCDWebServerBodyWriter>
|
||||
|
||||
/**
|
||||
* Returns the HTTP method for the request.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* method;
|
||||
|
||||
/**
|
||||
* Returns the URL for the request.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSURL* URL;
|
||||
|
||||
/**
|
||||
* Returns the HTTP headers for the request.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSDictionary* headers;
|
||||
|
||||
/**
|
||||
* Returns the path component of the URL for the request.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* path;
|
||||
|
||||
/**
|
||||
* Returns the parsed and unescaped query component of the URL for the request.
|
||||
*
|
||||
* @warning This property will be nil if there is no query in the URL.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDictionary* query;
|
||||
|
||||
/**
|
||||
* Returns the content type for the body of the request parsed from the
|
||||
* "Content-Type" header.
|
||||
*
|
||||
* This property will be nil if the request has no body or set to
|
||||
* "application/octet-stream" if a body is present but there was no
|
||||
* "Content-Type" header.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString* contentType;
|
||||
|
||||
/**
|
||||
* Returns the content length for the body of the request parsed from the
|
||||
* "Content-Length" header.
|
||||
*
|
||||
* This property will be set to "NSUIntegerMax" if the request has no body or
|
||||
* if there is a body but no "Content-Length" header, typically because
|
||||
* chunked transfer encoding is used.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSUInteger contentLength;
|
||||
|
||||
/**
|
||||
* Returns the parsed "If-Modified-Since" header or nil if absent or malformed.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSDate* ifModifiedSince;
|
||||
|
||||
/**
|
||||
* Returns the parsed "If-None-Match" header or nil if absent or malformed.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString* ifNoneMatch;
|
||||
|
||||
/**
|
||||
* Returns the parsed "Range" header or (NSUIntegerMax, 0) if absent or malformed.
|
||||
* The range will be set to (offset, length) if expressed from the beginning
|
||||
* of the entity body, or (NSUIntegerMax, length) if expressed from its end.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSRange byteRange;
|
||||
|
||||
/**
|
||||
* Returns YES if the client supports gzip content encoding according to the
|
||||
* "Accept-Encoding" header.
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL acceptsGzipContentEncoding;
|
||||
|
||||
/**
|
||||
* Returns the address of the local peer (i.e. server) for the request
|
||||
* as a raw "struct sockaddr".
|
||||
*/
|
||||
@property(nonatomic, readonly) NSData* localAddressData;
|
||||
|
||||
/**
|
||||
* Returns the address of the local peer (i.e. server) for the request
|
||||
* as a string.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* localAddressString;
|
||||
|
||||
/**
|
||||
* Returns the address of the remote peer (i.e. client) for the request
|
||||
* as a raw "struct sockaddr".
|
||||
*/
|
||||
@property(nonatomic, readonly) NSData* remoteAddressData;
|
||||
|
||||
/**
|
||||
* Returns the address of the remote peer (i.e. client) for the request
|
||||
* as a string.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* remoteAddressString;
|
||||
|
||||
/**
|
||||
* This method is the designated initializer for the class.
|
||||
*/
|
||||
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(nullable NSDictionary*)query;
|
||||
|
||||
/**
|
||||
* Convenience method that checks if the contentType property is defined.
|
||||
*/
|
||||
- (BOOL)hasBody;
|
||||
|
||||
/**
|
||||
* Convenience method that checks if the byteRange property is defined.
|
||||
*/
|
||||
- (BOOL)hasByteRange;
|
||||
|
||||
/**
|
||||
* Retrieves an attribute associated with this request using the given key.
|
||||
*
|
||||
* @return The attribute value for the key.
|
||||
*/
|
||||
- (nullable id)attributeForKey:(NSString*)key;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <zlib.h>
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerRequestAttribute_RegexCaptures";
|
||||
|
||||
#define kZlibErrorDomain @"ZlibErrorDomain"
|
||||
#define kGZipInitialBufferSize (256 * 1024)
|
||||
|
||||
@interface GCDWebServerBodyDecoder : NSObject <GCDWebServerBodyWriter>
|
||||
@end
|
||||
|
||||
@interface GCDWebServerGZipDecoder : GCDWebServerBodyDecoder
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerBodyDecoder {
|
||||
GCDWebServerRequest* __unsafe_unretained _request;
|
||||
id<GCDWebServerBodyWriter> __unsafe_unretained _writer;
|
||||
}
|
||||
|
||||
- (instancetype)initWithRequest:(GCDWebServerRequest* _Nonnull)request writer:(id<GCDWebServerBodyWriter> _Nonnull)writer {
|
||||
if ((self = [super init])) {
|
||||
_request = request;
|
||||
_writer = writer;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
return [_writer open:error];
|
||||
}
|
||||
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||
return [_writer writeData:data error:error];
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
return [_writer close:error];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerGZipDecoder {
|
||||
z_stream _stream;
|
||||
BOOL _finished;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
int result = inflateInit2(&_stream, 15 + 16);
|
||||
if (result != Z_OK) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
if (![super open:error]) {
|
||||
inflateEnd(&_stream);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||
GWS_DCHECK(!_finished);
|
||||
_stream.next_in = (Bytef*)data.bytes;
|
||||
_stream.avail_in = (uInt)data.length;
|
||||
NSMutableData* decodedData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize];
|
||||
if (decodedData == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
return NO;
|
||||
}
|
||||
NSUInteger length = 0;
|
||||
while (1) {
|
||||
NSUInteger maxLength = decodedData.length - length;
|
||||
_stream.next_out = (Bytef*)((char*)decodedData.mutableBytes + length);
|
||||
_stream.avail_out = (uInt)maxLength;
|
||||
int result = inflate(&_stream, Z_NO_FLUSH);
|
||||
if ((result != Z_OK) && (result != Z_STREAM_END)) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
length += maxLength - _stream.avail_out;
|
||||
if (_stream.avail_out > 0) {
|
||||
if (result == Z_STREAM_END) {
|
||||
_finished = YES;
|
||||
}
|
||||
break;
|
||||
}
|
||||
decodedData.length = 2 * decodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available
|
||||
}
|
||||
decodedData.length = length;
|
||||
BOOL success = length ? [super writeData:decodedData error:error] : YES; // No need to call writer if we have no data yet
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
GWS_DCHECK(_finished);
|
||||
inflateEnd(&_stream);
|
||||
return [super close:error];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerRequest {
|
||||
BOOL _opened;
|
||||
NSMutableArray* _decoders;
|
||||
id<GCDWebServerBodyWriter> __unsafe_unretained _writer;
|
||||
NSMutableDictionary* _attributes;
|
||||
}
|
||||
|
||||
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
||||
if ((self = [super init])) {
|
||||
_method = [method copy];
|
||||
_URL = url;
|
||||
_headers = headers;
|
||||
_path = [path copy];
|
||||
_query = query;
|
||||
|
||||
_contentType = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Content-Type"]);
|
||||
_usesChunkedTransferEncoding = [GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Transfer-Encoding"]) isEqualToString:@"chunked"];
|
||||
NSString* lengthHeader = [_headers objectForKey:@"Content-Length"];
|
||||
if (lengthHeader) {
|
||||
NSInteger length = [lengthHeader integerValue];
|
||||
if (_usesChunkedTransferEncoding || (length < 0)) {
|
||||
GWS_LOG_WARNING(@"Invalid 'Content-Length' header '%@' for '%@' request on \"%@\"", lengthHeader, _method, _URL);
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
_contentLength = length;
|
||||
if (_contentType == nil) {
|
||||
_contentType = kGCDWebServerDefaultMimeType;
|
||||
}
|
||||
} else if (_usesChunkedTransferEncoding) {
|
||||
if (_contentType == nil) {
|
||||
_contentType = kGCDWebServerDefaultMimeType;
|
||||
}
|
||||
_contentLength = NSUIntegerMax;
|
||||
} else {
|
||||
if (_contentType) {
|
||||
GWS_LOG_WARNING(@"Ignoring 'Content-Type' header for '%@' request on \"%@\"", _method, _URL);
|
||||
_contentType = nil; // Content-Type without Content-Length or chunked-encoding doesn't make sense
|
||||
}
|
||||
_contentLength = NSUIntegerMax;
|
||||
}
|
||||
|
||||
NSString* modifiedHeader = [_headers objectForKey:@"If-Modified-Since"];
|
||||
if (modifiedHeader) {
|
||||
_ifModifiedSince = [GCDWebServerParseRFC822(modifiedHeader) copy];
|
||||
}
|
||||
_ifNoneMatch = [_headers objectForKey:@"If-None-Match"];
|
||||
|
||||
_byteRange = NSMakeRange(NSUIntegerMax, 0);
|
||||
NSString* rangeHeader = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Range"]);
|
||||
if (rangeHeader) {
|
||||
if ([rangeHeader hasPrefix:@"bytes="]) {
|
||||
NSArray* components = [[rangeHeader substringFromIndex:6] componentsSeparatedByString:@","];
|
||||
if (components.count == 1) {
|
||||
components = [[components firstObject] componentsSeparatedByString:@"-"];
|
||||
if (components.count == 2) {
|
||||
NSString* startString = [components objectAtIndex:0];
|
||||
NSInteger startValue = [startString integerValue];
|
||||
NSString* endString = [components objectAtIndex:1];
|
||||
NSInteger endValue = [endString integerValue];
|
||||
if (startString.length && (startValue >= 0) && endString.length && (endValue >= startValue)) { // The second 500 bytes: "500-999"
|
||||
_byteRange.location = startValue;
|
||||
_byteRange.length = endValue - startValue + 1;
|
||||
} else if (startString.length && (startValue >= 0)) { // The bytes after 9500 bytes: "9500-"
|
||||
_byteRange.location = startValue;
|
||||
_byteRange.length = NSUIntegerMax;
|
||||
} else if (endString.length && (endValue > 0)) { // The final 500 bytes: "-500"
|
||||
_byteRange.location = NSUIntegerMax;
|
||||
_byteRange.length = endValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((_byteRange.location == NSUIntegerMax) && (_byteRange.length == 0)) { // Ignore "Range" header if syntactically invalid
|
||||
GWS_LOG_WARNING(@"Failed to parse 'Range' header \"%@\" for url: %@", rangeHeader, url);
|
||||
}
|
||||
}
|
||||
|
||||
if ([[_headers objectForKey:@"Accept-Encoding"] rangeOfString:@"gzip"].location != NSNotFound) {
|
||||
_acceptsGzipContentEncoding = YES;
|
||||
}
|
||||
|
||||
_decoders = [[NSMutableArray alloc] init];
|
||||
_attributes = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)hasBody {
|
||||
return _contentType ? YES : NO;
|
||||
}
|
||||
|
||||
- (BOOL)hasByteRange {
|
||||
return GCDWebServerIsValidByteRange(_byteRange);
|
||||
}
|
||||
|
||||
- (id)attributeForKey:(NSString*)key {
|
||||
return [_attributes objectForKey:key];
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)prepareForWriting {
|
||||
_writer = self;
|
||||
if ([GCDWebServerNormalizeHeaderValue([self.headers objectForKey:@"Content-Encoding"]) isEqualToString:@"gzip"]) {
|
||||
GCDWebServerGZipDecoder* decoder = [[GCDWebServerGZipDecoder alloc] initWithRequest:self writer:_writer];
|
||||
[_decoders addObject:decoder];
|
||||
_writer = decoder;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)performOpen:(NSError**)error {
|
||||
GWS_DCHECK(_contentType);
|
||||
GWS_DCHECK(_writer);
|
||||
if (_opened) {
|
||||
GWS_DNOT_REACHED();
|
||||
return NO;
|
||||
}
|
||||
_opened = YES;
|
||||
return [_writer open:error];
|
||||
}
|
||||
|
||||
- (BOOL)performWriteData:(NSData*)data error:(NSError**)error {
|
||||
GWS_DCHECK(_opened);
|
||||
return [_writer writeData:data error:error];
|
||||
}
|
||||
|
||||
- (BOOL)performClose:(NSError**)error {
|
||||
GWS_DCHECK(_opened);
|
||||
return [_writer close:error];
|
||||
}
|
||||
|
||||
- (void)setAttribute:(id)attribute forKey:(NSString*)key {
|
||||
[_attributes setValue:attribute forKey:key];
|
||||
}
|
||||
|
||||
- (NSString*)localAddressString {
|
||||
return GCDWebServerStringFromSockAddr(_localAddressData.bytes, YES);
|
||||
}
|
||||
|
||||
- (NSString*)remoteAddressString {
|
||||
return GCDWebServerStringFromSockAddr(_remoteAddressData.bytes, YES);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithFormat:@"%@ %@", _method, _path];
|
||||
for (NSString* argument in [[_query allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
|
||||
[description appendFormat:@"\n %@ = %@", argument, [_query objectForKey:argument]];
|
||||
}
|
||||
[description appendString:@"\n"];
|
||||
for (NSString* header in [[_headers allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
|
||||
[description appendFormat:@"\n%@: %@", header, [_headers objectForKey:header]];
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerBodyReaderCompletionBlock is passed by GCDWebServer to the
|
||||
* GCDWebServerBodyReader object when reading data from it asynchronously.
|
||||
*/
|
||||
typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* _Nullable error);
|
||||
|
||||
/**
|
||||
* This protocol is used by the GCDWebServerConnection to communicate with
|
||||
* the GCDWebServerResponse and read the HTTP body data to send.
|
||||
*
|
||||
* Note that multiple GCDWebServerBodyReader objects can be chained together
|
||||
* internally e.g. to automatically apply gzip encoding to the content before
|
||||
* passing it on to the GCDWebServerResponse.
|
||||
*
|
||||
* @warning These methods can be called on any GCD thread.
|
||||
*/
|
||||
@protocol GCDWebServerBodyReader <NSObject>
|
||||
|
||||
@required
|
||||
|
||||
/**
|
||||
* This method is called before any body data is sent.
|
||||
*
|
||||
* It should return YES on success or NO on failure and set the "error" argument
|
||||
* which is guaranteed to be non-NULL.
|
||||
*/
|
||||
- (BOOL)open:(NSError**)error;
|
||||
|
||||
/**
|
||||
* This method is called whenever body data is sent.
|
||||
*
|
||||
* It should return a non-empty NSData if there is body data available,
|
||||
* or an empty NSData there is no more body data, or nil on error and set
|
||||
* the "error" argument which is guaranteed to be non-NULL.
|
||||
*/
|
||||
- (nullable NSData*)readData:(NSError**)error;
|
||||
|
||||
/**
|
||||
* This method is called after all body data has been sent.
|
||||
*/
|
||||
- (void)close;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* If this method is implemented, it will be preferred over -readData:.
|
||||
*
|
||||
* It must call the passed block when data is available, passing a non-empty
|
||||
* NSData if there is body data available, or an empty NSData there is no more
|
||||
* body data, or nil on error and pass an NSError along.
|
||||
*/
|
||||
- (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The GCDWebServerResponse class is used to wrap a single HTTP response.
|
||||
* It is instantiated by the handler of the GCDWebServer that handled the request.
|
||||
* If a body is present, the methods from the GCDWebServerBodyReader protocol
|
||||
* will be called by the GCDWebServerConnection to send it.
|
||||
*
|
||||
* The default implementation of the GCDWebServerBodyReader protocol
|
||||
* on the class simply returns an empty body.
|
||||
*
|
||||
* @warning GCDWebServerResponse instances can be created and used on any GCD thread.
|
||||
*/
|
||||
@interface GCDWebServerResponse : NSObject <GCDWebServerBodyReader>
|
||||
|
||||
/**
|
||||
* Sets the content type for the body of the response.
|
||||
*
|
||||
* The default value is nil i.e. the response has no body.
|
||||
*
|
||||
* @warning This property must be set if a body is present.
|
||||
*/
|
||||
@property(nonatomic, copy, nullable) NSString* contentType;
|
||||
|
||||
/**
|
||||
* Sets the content length for the body of the response. If a body is present
|
||||
* but this property is set to "NSUIntegerMax", this means the length of the body
|
||||
* cannot be known ahead of time. Chunked transfer encoding will be
|
||||
* automatically enabled by the GCDWebServerConnection to comply with HTTP/1.1
|
||||
* specifications.
|
||||
*
|
||||
* The default value is "NSUIntegerMax" i.e. the response has no body or its length
|
||||
* is undefined.
|
||||
*/
|
||||
@property(nonatomic) NSUInteger contentLength;
|
||||
|
||||
/**
|
||||
* Sets the HTTP status code for the response.
|
||||
*
|
||||
* The default value is 200 i.e. "OK".
|
||||
*/
|
||||
@property(nonatomic) NSInteger statusCode;
|
||||
|
||||
/**
|
||||
* Sets the caching hint for the response using the "Cache-Control" header.
|
||||
* This value is expressed in seconds.
|
||||
*
|
||||
* The default value is 0 i.e. "no-cache".
|
||||
*/
|
||||
@property(nonatomic) NSUInteger cacheControlMaxAge;
|
||||
|
||||
/**
|
||||
* Sets the last modified date for the response using the "Last-Modified" header.
|
||||
*
|
||||
* The default value is nil.
|
||||
*/
|
||||
@property(nonatomic, nullable) NSDate* lastModifiedDate;
|
||||
|
||||
/**
|
||||
* Sets the ETag for the response using the "ETag" header.
|
||||
*
|
||||
* The default value is nil.
|
||||
*/
|
||||
@property(nonatomic, copy, nullable) NSString* eTag;
|
||||
|
||||
/**
|
||||
* Enables gzip encoding for the response body.
|
||||
*
|
||||
* The default value is NO.
|
||||
*
|
||||
* @warning Enabling gzip encoding will remove any "Content-Length" header
|
||||
* since the length of the body is not known anymore. The client will still
|
||||
* be able to determine the body length when connection is closed per
|
||||
* HTTP/1.1 specifications.
|
||||
*/
|
||||
@property(nonatomic, getter=isGZipContentEncodingEnabled) BOOL gzipContentEncodingEnabled;
|
||||
|
||||
/**
|
||||
* Creates an empty response.
|
||||
*/
|
||||
+ (instancetype)response;
|
||||
|
||||
/**
|
||||
* This method is the designated initializer for the class.
|
||||
*/
|
||||
- (instancetype)init;
|
||||
|
||||
/**
|
||||
* Sets an additional HTTP header on the response.
|
||||
* Pass a nil value to remove an additional header.
|
||||
*
|
||||
* @warning Do not attempt to override the primary headers used
|
||||
* by GCDWebServerResponse like "Content-Type", "ETag", etc...
|
||||
*/
|
||||
- (void)setValue:(nullable NSString*)value forAdditionalHeader:(NSString*)header;
|
||||
|
||||
/**
|
||||
* Convenience method that checks if the contentType property is defined.
|
||||
*/
|
||||
- (BOOL)hasBody;
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerResponse (Extensions)
|
||||
|
||||
/**
|
||||
* Creates a empty response with a specific HTTP status code.
|
||||
*/
|
||||
+ (instancetype)responseWithStatusCode:(NSInteger)statusCode;
|
||||
|
||||
/**
|
||||
* Creates an HTTP redirect response to a new URL.
|
||||
*/
|
||||
+ (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
|
||||
|
||||
/**
|
||||
* Initializes an empty response with a specific HTTP status code.
|
||||
*/
|
||||
- (instancetype)initWithStatusCode:(NSInteger)statusCode;
|
||||
|
||||
/**
|
||||
* Initializes an HTTP redirect response to a new URL.
|
||||
*/
|
||||
- (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <zlib.h>
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
#define kZlibErrorDomain @"ZlibErrorDomain"
|
||||
#define kGZipInitialBufferSize (256 * 1024)
|
||||
|
||||
@interface GCDWebServerBodyEncoder : NSObject <GCDWebServerBodyReader>
|
||||
@end
|
||||
|
||||
@interface GCDWebServerGZipEncoder : GCDWebServerBodyEncoder
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerBodyEncoder {
|
||||
GCDWebServerResponse* __unsafe_unretained _response;
|
||||
id<GCDWebServerBodyReader> __unsafe_unretained _reader;
|
||||
}
|
||||
|
||||
- (instancetype)initWithResponse:(GCDWebServerResponse* _Nonnull)response reader:(id<GCDWebServerBodyReader> _Nonnull)reader {
|
||||
if ((self = [super init])) {
|
||||
_response = response;
|
||||
_reader = reader;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
return [_reader open:error];
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
return [_reader readData:error];
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
[_reader close];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerGZipEncoder {
|
||||
z_stream _stream;
|
||||
BOOL _finished;
|
||||
}
|
||||
|
||||
- (instancetype)initWithResponse:(GCDWebServerResponse* _Nonnull)response reader:(id<GCDWebServerBodyReader> _Nonnull)reader {
|
||||
if ((self = [super initWithResponse:response reader:reader])) {
|
||||
response.contentLength = NSUIntegerMax; // Make sure "Content-Length" header is not set since we don't know it
|
||||
[response setValue:@"gzip" forAdditionalHeader:@"Content-Encoding"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
int result = deflateInit2(&_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
|
||||
if (result != Z_OK) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
if (![super open:error]) {
|
||||
deflateEnd(&_stream);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
NSMutableData* encodedData;
|
||||
if (_finished) {
|
||||
encodedData = [[NSMutableData alloc] init];
|
||||
} else {
|
||||
encodedData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize];
|
||||
if (encodedData == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
NSUInteger length = 0;
|
||||
do {
|
||||
NSData* data = [super readData:error];
|
||||
if (data == nil) {
|
||||
return nil;
|
||||
}
|
||||
_stream.next_in = (Bytef*)data.bytes;
|
||||
_stream.avail_in = (uInt)data.length;
|
||||
while (1) {
|
||||
NSUInteger maxLength = encodedData.length - length;
|
||||
_stream.next_out = (Bytef*)((char*)encodedData.mutableBytes + length);
|
||||
_stream.avail_out = (uInt)maxLength;
|
||||
int result = deflate(&_stream, data.length ? Z_NO_FLUSH : Z_FINISH);
|
||||
if (result == Z_STREAM_END) {
|
||||
_finished = YES;
|
||||
} else if (result != Z_OK) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
length += maxLength - _stream.avail_out;
|
||||
if (_stream.avail_out > 0) {
|
||||
break;
|
||||
}
|
||||
encodedData.length = 2 * encodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available
|
||||
}
|
||||
GWS_DCHECK(_stream.avail_in == 0);
|
||||
} while (length == 0); // Make sure we don't return an empty NSData if not in finished state
|
||||
encodedData.length = length;
|
||||
}
|
||||
return encodedData;
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
deflateEnd(&_stream);
|
||||
[super close];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerResponse {
|
||||
BOOL _opened;
|
||||
NSMutableArray* _encoders;
|
||||
id<GCDWebServerBodyReader> __unsafe_unretained _reader;
|
||||
}
|
||||
|
||||
+ (instancetype)response {
|
||||
return [[[self class] alloc] init];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if ((self = [super init])) {
|
||||
_contentType = nil;
|
||||
_contentLength = NSUIntegerMax;
|
||||
_statusCode = kGCDWebServerHTTPStatusCode_OK;
|
||||
_cacheControlMaxAge = 0;
|
||||
_additionalHeaders = [[NSMutableDictionary alloc] init];
|
||||
_encoders = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header {
|
||||
[_additionalHeaders setValue:value forKey:header];
|
||||
}
|
||||
|
||||
- (BOOL)hasBody {
|
||||
return _contentType ? YES : NO;
|
||||
}
|
||||
|
||||
- (BOOL)usesChunkedTransferEncoding {
|
||||
return (_contentType != nil) && (_contentLength == NSUIntegerMax);
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
return [NSData data];
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
;
|
||||
}
|
||||
|
||||
- (void)prepareForReading {
|
||||
_reader = self;
|
||||
if (_gzipContentEncodingEnabled) {
|
||||
GCDWebServerGZipEncoder* encoder = [[GCDWebServerGZipEncoder alloc] initWithResponse:self reader:_reader];
|
||||
[_encoders addObject:encoder];
|
||||
_reader = encoder;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)performOpen:(NSError**)error {
|
||||
GWS_DCHECK(_contentType);
|
||||
GWS_DCHECK(_reader);
|
||||
if (_opened) {
|
||||
GWS_DNOT_REACHED();
|
||||
return NO;
|
||||
}
|
||||
_opened = YES;
|
||||
return [_reader open:error];
|
||||
}
|
||||
|
||||
- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block {
|
||||
GWS_DCHECK(_opened);
|
||||
if ([_reader respondsToSelector:@selector(asyncReadDataWithCompletion:)]) {
|
||||
[_reader asyncReadDataWithCompletion:[block copy]];
|
||||
} else {
|
||||
NSError* error = nil;
|
||||
NSData* data = [_reader readData:&error];
|
||||
block(data, error);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)performClose {
|
||||
GWS_DCHECK(_opened);
|
||||
[_reader close];
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithFormat:@"Status Code = %i", (int)_statusCode];
|
||||
if (_contentType) {
|
||||
[description appendFormat:@"\nContent Type = %@", _contentType];
|
||||
}
|
||||
if (_contentLength != NSUIntegerMax) {
|
||||
[description appendFormat:@"\nContent Length = %lu", (unsigned long)_contentLength];
|
||||
}
|
||||
[description appendFormat:@"\nCache Control Max Age = %lu", (unsigned long)_cacheControlMaxAge];
|
||||
if (_lastModifiedDate) {
|
||||
[description appendFormat:@"\nLast Modified Date = %@", _lastModifiedDate];
|
||||
}
|
||||
if (_eTag) {
|
||||
[description appendFormat:@"\nETag = %@", _eTag];
|
||||
}
|
||||
if (_additionalHeaders.count) {
|
||||
[description appendString:@"\n"];
|
||||
for (NSString* header in [[_additionalHeaders allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
|
||||
[description appendFormat:@"\n%@: %@", header, [_additionalHeaders objectForKey:header]];
|
||||
}
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerResponse (Extensions)
|
||||
|
||||
+ (instancetype)responseWithStatusCode:(NSInteger)statusCode {
|
||||
return [[self alloc] initWithStatusCode:statusCode];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent {
|
||||
return [[self alloc] initWithRedirect:location permanent:permanent];
|
||||
}
|
||||
|
||||
- (instancetype)initWithStatusCode:(NSInteger)statusCode {
|
||||
if ((self = [self init])) {
|
||||
self.statusCode = statusCode;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent {
|
||||
if ((self = [self init])) {
|
||||
self.statusCode = permanent ? kGCDWebServerHTTPStatusCode_MovedPermanently : kGCDWebServerHTTPStatusCode_TemporaryRedirect;
|
||||
[self setValue:[location absoluteString] forAdditionalHeader:@"Location"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerRequest.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerDataRequest subclass of GCDWebServerRequest stores the body
|
||||
* of the HTTP request in memory.
|
||||
*/
|
||||
@interface GCDWebServerDataRequest : GCDWebServerRequest
|
||||
|
||||
/**
|
||||
* Returns the data for the request body.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSData* data;
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerDataRequest (Extensions)
|
||||
|
||||
/**
|
||||
* Returns the data for the request body interpreted as text. If the content
|
||||
* type of the body is not a text one, or if an error occurs, nil is returned.
|
||||
*
|
||||
* The text encoding used to interpret the data is extracted from the
|
||||
* "Content-Type" header or defaults to UTF-8.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString* text;
|
||||
|
||||
/**
|
||||
* Returns the data for the request body interpreted as a JSON object. If the
|
||||
* content type of the body is not JSON, or if an error occurs, nil is returned.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) id jsonObject;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@interface GCDWebServerDataRequest ()
|
||||
@property(nonatomic) NSMutableData* data;
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerDataRequest {
|
||||
NSString* _text;
|
||||
id _jsonObject;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
if (self.contentLength != NSUIntegerMax) {
|
||||
_data = [[NSMutableData alloc] initWithCapacity:self.contentLength];
|
||||
} else {
|
||||
_data = [[NSMutableData alloc] init];
|
||||
}
|
||||
if (_data == nil) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed allocating memory" }];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||
[_data appendData:data];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithString:[super description]];
|
||||
if (_data) {
|
||||
[description appendString:@"\n\n"];
|
||||
[description appendString:GCDWebServerDescribeData(_data, (NSString*)self.contentType)];
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerDataRequest (Extensions)
|
||||
|
||||
- (NSString*)text {
|
||||
if (_text == nil) {
|
||||
if ([self.contentType hasPrefix:@"text/"]) {
|
||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset");
|
||||
_text = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
}
|
||||
return _text;
|
||||
}
|
||||
|
||||
- (id)jsonObject {
|
||||
if (_jsonObject == nil) {
|
||||
NSString* mimeType = GCDWebServerTruncateHeaderValue(self.contentType);
|
||||
if ([mimeType isEqualToString:@"application/json"] || [mimeType isEqualToString:@"text/json"] || [mimeType isEqualToString:@"text/javascript"]) {
|
||||
_jsonObject = [NSJSONSerialization JSONObjectWithData:_data options:0 error:NULL];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
}
|
||||
return _jsonObject;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerRequest.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerFileRequest subclass of GCDWebServerRequest stores the body
|
||||
* of the HTTP request to a file on disk.
|
||||
*/
|
||||
@interface GCDWebServerFileRequest : GCDWebServerRequest
|
||||
|
||||
/**
|
||||
* Returns the path to the temporary file containing the request body.
|
||||
*
|
||||
* @warning This temporary file will be automatically deleted when the
|
||||
* GCDWebServerFileRequest is deallocated. If you want to preserve this file,
|
||||
* you must move it to a different location beforehand.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* temporaryPath;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@implementation GCDWebServerFileRequest {
|
||||
int _file;
|
||||
}
|
||||
|
||||
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
||||
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
|
||||
_temporaryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
unlink([_temporaryPath fileSystemRepresentation]);
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
_file = open([_temporaryPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (_file <= 0) {
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||
if (write(_file, data.bytes, data.length) != (ssize_t)data.length) {
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
if (close(_file) < 0) {
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
#ifdef __GCDWEBSERVER_ENABLE_TESTING__
|
||||
NSString* creationDateHeader = [self.headers objectForKey:@"X-GCDWebServer-CreationDate"];
|
||||
if (creationDateHeader) {
|
||||
NSDate* date = GCDWebServerParseISO8601(creationDateHeader);
|
||||
if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileCreationDate : date} ofItemAtPath:_temporaryPath error:error]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
NSString* modifiedDateHeader = [self.headers objectForKey:@"X-GCDWebServer-ModifiedDate"];
|
||||
if (modifiedDateHeader) {
|
||||
NSDate* date = GCDWebServerParseRFC822(modifiedDateHeader);
|
||||
if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileModificationDate : date} ofItemAtPath:_temporaryPath error:error]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithString:[super description]];
|
||||
[description appendFormat:@"\n\n{%@}", _temporaryPath];
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerRequest.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerMultiPart class is an abstract class that wraps the content
|
||||
* of a part.
|
||||
*/
|
||||
@interface GCDWebServerMultiPart : NSObject
|
||||
|
||||
/**
|
||||
* Returns the control name retrieved from the part headers.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* controlName;
|
||||
|
||||
/**
|
||||
* Returns the content type retrieved from the part headers or "text/plain"
|
||||
* if not available (per HTTP specifications).
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* contentType;
|
||||
|
||||
/**
|
||||
* Returns the MIME type component of the content type for the part.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* mimeType;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The GCDWebServerMultiPartArgument subclass of GCDWebServerMultiPart wraps
|
||||
* the content of a part as data in memory.
|
||||
*/
|
||||
@interface GCDWebServerMultiPartArgument : GCDWebServerMultiPart
|
||||
|
||||
/**
|
||||
* Returns the data for the part.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSData* data;
|
||||
|
||||
/**
|
||||
* Returns the data for the part interpreted as text. If the content
|
||||
* type of the part is not a text one, or if an error occurs, nil is returned.
|
||||
*
|
||||
* The text encoding used to interpret the data is extracted from the
|
||||
* "Content-Type" header or defaults to UTF-8.
|
||||
*/
|
||||
@property(nonatomic, readonly, nullable) NSString* string;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The GCDWebServerMultiPartFile subclass of GCDWebServerMultiPart wraps
|
||||
* the content of a part as a file on disk.
|
||||
*/
|
||||
@interface GCDWebServerMultiPartFile : GCDWebServerMultiPart
|
||||
|
||||
/**
|
||||
* Returns the file name retrieved from the part headers.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* fileName;
|
||||
|
||||
/**
|
||||
* Returns the path to the temporary file containing the part data.
|
||||
*
|
||||
* @warning This temporary file will be automatically deleted when the
|
||||
* GCDWebServerMultiPartFile is deallocated. If you want to preserve this file,
|
||||
* you must move it to a different location beforehand.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* temporaryPath;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The GCDWebServerMultiPartFormRequest subclass of GCDWebServerRequest
|
||||
* parses the body of the HTTP request as a multipart encoded form.
|
||||
*/
|
||||
@interface GCDWebServerMultiPartFormRequest : GCDWebServerRequest
|
||||
|
||||
/**
|
||||
* Returns the argument parts from the multipart encoded form as
|
||||
* name / GCDWebServerMultiPartArgument pairs.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSArray* arguments;
|
||||
|
||||
/**
|
||||
* Returns the files parts from the multipart encoded form as
|
||||
* name / GCDWebServerMultiPartFile pairs.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSArray* files;
|
||||
|
||||
/**
|
||||
* Returns the MIME type for multipart encoded forms
|
||||
* i.e. "multipart/form-data".
|
||||
*/
|
||||
+ (NSString*)mimeType;
|
||||
|
||||
/**
|
||||
* Returns the first argument for a given control name or nil if not found.
|
||||
*/
|
||||
- (nullable GCDWebServerMultiPartArgument*)firstArgumentForControlName:(NSString*)name;
|
||||
|
||||
/**
|
||||
* Returns the first file for a given control name or nil if not found.
|
||||
*/
|
||||
- (nullable GCDWebServerMultiPartFile*)firstFileForControlName:(NSString*)name;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
#define kMultiPartBufferSize (256 * 1024)
|
||||
|
||||
typedef enum {
|
||||
kParserState_Undefined = 0,
|
||||
kParserState_Start,
|
||||
kParserState_Headers,
|
||||
kParserState_Content,
|
||||
kParserState_End
|
||||
} ParserState;
|
||||
|
||||
@interface GCDWebServerMIMEStreamParser : NSObject
|
||||
@end
|
||||
|
||||
static NSData* _newlineData = nil;
|
||||
static NSData* _newlinesData = nil;
|
||||
static NSData* _dashNewlineData = nil;
|
||||
|
||||
@implementation GCDWebServerMultiPart
|
||||
|
||||
- (instancetype)initWithControlName:(NSString* _Nonnull)name contentType:(NSString* _Nonnull)type {
|
||||
if ((self = [super init])) {
|
||||
_controlName = [name copy];
|
||||
_contentType = [type copy];
|
||||
_mimeType = (NSString*)GCDWebServerTruncateHeaderValue(_contentType);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMultiPartArgument
|
||||
|
||||
- (instancetype)initWithControlName:(NSString* _Nonnull)name contentType:(NSString* _Nonnull)type data:(NSData* _Nonnull)data {
|
||||
if ((self = [super initWithControlName:name contentType:type])) {
|
||||
_data = data;
|
||||
|
||||
if ([self.contentType hasPrefix:@"text/"]) {
|
||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset");
|
||||
_string = [[NSString alloc] initWithData:_data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
return [NSString stringWithFormat:@"<%@ | '%@' | %lu bytes>", [self class], self.mimeType, (unsigned long)_data.length];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMultiPartFile
|
||||
|
||||
- (instancetype)initWithControlName:(NSString* _Nonnull)name contentType:(NSString* _Nonnull)type fileName:(NSString* _Nonnull)fileName temporaryPath:(NSString* _Nonnull)temporaryPath {
|
||||
if ((self = [super initWithControlName:name contentType:type])) {
|
||||
_fileName = [fileName copy];
|
||||
_temporaryPath = [temporaryPath copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
unlink([_temporaryPath fileSystemRepresentation]);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
return [NSString stringWithFormat:@"<%@ | '%@' | '%@>'", [self class], self.mimeType, _fileName];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMIMEStreamParser {
|
||||
NSData* _boundary;
|
||||
NSString* _defaultcontrolName;
|
||||
ParserState _state;
|
||||
NSMutableData* _data;
|
||||
NSMutableArray* _arguments;
|
||||
NSMutableArray* _files;
|
||||
|
||||
NSString* _controlName;
|
||||
NSString* _fileName;
|
||||
NSString* _contentType;
|
||||
NSString* _tmpPath;
|
||||
int _tmpFile;
|
||||
GCDWebServerMIMEStreamParser* _subParser;
|
||||
}
|
||||
|
||||
+ (void)initialize {
|
||||
if (_newlineData == nil) {
|
||||
_newlineData = [[NSData alloc] initWithBytes:"\r\n" length:2];
|
||||
GWS_DCHECK(_newlineData);
|
||||
}
|
||||
if (_newlinesData == nil) {
|
||||
_newlinesData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
|
||||
GWS_DCHECK(_newlinesData);
|
||||
}
|
||||
if (_dashNewlineData == nil) {
|
||||
_dashNewlineData = [[NSData alloc] initWithBytes:"--\r\n" length:4];
|
||||
GWS_DCHECK(_dashNewlineData);
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)initWithBoundary:(NSString* _Nonnull)boundary defaultControlName:(NSString* _Nullable)name arguments:(NSMutableArray* _Nonnull)arguments files:(NSMutableArray* _Nonnull)files {
|
||||
NSData* data = boundary.length ? [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding] : nil;
|
||||
if (data == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
if ((self = [super init])) {
|
||||
_boundary = data;
|
||||
_defaultcontrolName = name;
|
||||
_arguments = arguments;
|
||||
_files = files;
|
||||
_data = [[NSMutableData alloc] initWithCapacity:kMultiPartBufferSize];
|
||||
_state = kParserState_Start;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (_tmpFile > 0) {
|
||||
close(_tmpFile);
|
||||
unlink([_tmpPath fileSystemRepresentation]);
|
||||
}
|
||||
}
|
||||
|
||||
// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
|
||||
- (BOOL)_parseData {
|
||||
BOOL success = YES;
|
||||
|
||||
if (_state == kParserState_Headers) {
|
||||
NSRange range = [_data rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _data.length)];
|
||||
if (range.location != NSNotFound) {
|
||||
_controlName = nil;
|
||||
_fileName = nil;
|
||||
_contentType = nil;
|
||||
_tmpPath = nil;
|
||||
_subParser = nil;
|
||||
NSString* headers = [[NSString alloc] initWithData:[_data subdataWithRange:NSMakeRange(0, range.location)] encoding:NSUTF8StringEncoding];
|
||||
if (headers) {
|
||||
for (NSString* header in [headers componentsSeparatedByString:@"\r\n"]) {
|
||||
NSRange subRange = [header rangeOfString:@":"];
|
||||
if (subRange.location != NSNotFound) {
|
||||
NSString* name = [header substringToIndex:subRange.location];
|
||||
NSString* value = [[header substringFromIndex:(subRange.location + subRange.length)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
if ([name caseInsensitiveCompare:@"Content-Type"] == NSOrderedSame) {
|
||||
_contentType = GCDWebServerNormalizeHeaderValue(value);
|
||||
} else if ([name caseInsensitiveCompare:@"Content-Disposition"] == NSOrderedSame) {
|
||||
NSString* contentDisposition = GCDWebServerNormalizeHeaderValue(value);
|
||||
if ([GCDWebServerTruncateHeaderValue(contentDisposition) isEqualToString:@"form-data"]) {
|
||||
_controlName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"name");
|
||||
_fileName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename");
|
||||
} else if ([GCDWebServerTruncateHeaderValue(contentDisposition) isEqualToString:@"file"]) {
|
||||
_controlName = _defaultcontrolName;
|
||||
_fileName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
}
|
||||
if (_contentType == nil) {
|
||||
_contentType = @"text/plain";
|
||||
}
|
||||
} else {
|
||||
GWS_LOG_ERROR(@"Failed decoding headers in part of 'multipart/form-data'");
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
if (_controlName) {
|
||||
if ([GCDWebServerTruncateHeaderValue(_contentType) isEqualToString:@"multipart/mixed"]) {
|
||||
NSString* boundary = GCDWebServerExtractHeaderValueParameter(_contentType, @"boundary");
|
||||
_subParser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:_controlName arguments:_arguments files:_files];
|
||||
if (_subParser == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
} else if (_fileName) {
|
||||
NSString* path = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
_tmpFile = open([path fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (_tmpFile > 0) {
|
||||
_tmpPath = [path copy];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
|
||||
[_data replaceBytesInRange:NSMakeRange(0, range.location + range.length) withBytes:NULL length:0];
|
||||
_state = kParserState_Content;
|
||||
}
|
||||
}
|
||||
|
||||
if ((_state == kParserState_Start) || (_state == kParserState_Content)) {
|
||||
NSRange range = [_data rangeOfData:_boundary options:0 range:NSMakeRange(0, _data.length)];
|
||||
if (range.location != NSNotFound) {
|
||||
NSRange subRange = NSMakeRange(range.location + range.length, _data.length - range.location - range.length);
|
||||
NSRange subRange1 = [_data rangeOfData:_newlineData options:NSDataSearchAnchored range:subRange];
|
||||
NSRange subRange2 = [_data rangeOfData:_dashNewlineData options:NSDataSearchAnchored range:subRange];
|
||||
if ((subRange1.location != NSNotFound) || (subRange2.location != NSNotFound)) {
|
||||
if (_state == kParserState_Content) {
|
||||
const void* dataBytes = _data.bytes;
|
||||
NSUInteger dataLength = range.location - 2;
|
||||
if (_subParser) {
|
||||
if (![_subParser appendBytes:dataBytes length:(dataLength + 2)] || ![_subParser isAtEnd]) {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
_subParser = nil;
|
||||
} else if (_tmpPath) {
|
||||
ssize_t result = write(_tmpFile, dataBytes, dataLength);
|
||||
if (result == (ssize_t)dataLength) {
|
||||
if (close(_tmpFile) == 0) {
|
||||
_tmpFile = 0;
|
||||
GCDWebServerMultiPartFile* file = [[GCDWebServerMultiPartFile alloc] initWithControlName:_controlName contentType:_contentType fileName:_fileName temporaryPath:_tmpPath];
|
||||
[_files addObject:file];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
_tmpPath = nil;
|
||||
} else {
|
||||
NSData* data = [[NSData alloc] initWithBytes:(void*)dataBytes length:dataLength];
|
||||
GCDWebServerMultiPartArgument* argument = [[GCDWebServerMultiPartArgument alloc] initWithControlName:_controlName contentType:_contentType data:data];
|
||||
[_arguments addObject:argument];
|
||||
}
|
||||
}
|
||||
|
||||
if (subRange1.location != NSNotFound) {
|
||||
[_data replaceBytesInRange:NSMakeRange(0, subRange1.location + subRange1.length) withBytes:NULL length:0];
|
||||
_state = kParserState_Headers;
|
||||
success = [self _parseData];
|
||||
} else {
|
||||
_state = kParserState_End;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NSUInteger margin = 2 * _boundary.length;
|
||||
if (_data.length > margin) {
|
||||
NSUInteger length = _data.length - margin;
|
||||
if (_subParser) {
|
||||
if ([_subParser appendBytes:_data.bytes length:length]) {
|
||||
[_data replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
} else if (_tmpPath) {
|
||||
ssize_t result = write(_tmpFile, _data.bytes, length);
|
||||
if (result == (ssize_t)length) {
|
||||
[_data replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)appendBytes:(const void*)bytes length:(NSUInteger)length {
|
||||
[_data appendBytes:bytes length:length];
|
||||
return [self _parseData];
|
||||
}
|
||||
|
||||
- (BOOL)isAtEnd {
|
||||
return (_state == kParserState_End);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerMultiPartFormRequest ()
|
||||
@property(nonatomic) NSMutableArray* arguments;
|
||||
@property(nonatomic) NSMutableArray* files;
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMultiPartFormRequest {
|
||||
GCDWebServerMIMEStreamParser* _parser;
|
||||
}
|
||||
|
||||
+ (NSString*)mimeType {
|
||||
return @"multipart/form-data";
|
||||
}
|
||||
|
||||
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
||||
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
|
||||
_arguments = [[NSMutableArray alloc] init];
|
||||
_files = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
NSString* boundary = GCDWebServerExtractHeaderValueParameter(self.contentType, @"boundary");
|
||||
_parser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:nil arguments:_arguments files:_files];
|
||||
if (_parser == nil) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed starting to parse multipart form data" }];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)writeData:(NSData*)data error:(NSError**)error {
|
||||
if (![_parser appendBytes:data.bytes length:data.length]) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed continuing to parse multipart form data" }];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
BOOL atEnd = [_parser isAtEnd];
|
||||
_parser = nil;
|
||||
if (!atEnd) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed finishing to parse multipart form data" }];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (GCDWebServerMultiPartArgument*)firstArgumentForControlName:(NSString*)name {
|
||||
for (GCDWebServerMultiPartArgument* argument in _arguments) {
|
||||
if ([argument.controlName isEqualToString:name]) {
|
||||
return argument;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (GCDWebServerMultiPartFile*)firstFileForControlName:(NSString*)name {
|
||||
for (GCDWebServerMultiPartFile* file in _files) {
|
||||
if ([file.controlName isEqualToString:name]) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithString:[super description]];
|
||||
if (_arguments.count) {
|
||||
[description appendString:@"\n"];
|
||||
for (GCDWebServerMultiPartArgument* argument in _arguments) {
|
||||
[description appendFormat:@"\n%@ (%@)\n", argument.controlName, argument.contentType];
|
||||
[description appendString:GCDWebServerDescribeData(argument.data, argument.contentType)];
|
||||
}
|
||||
}
|
||||
if (_files.count) {
|
||||
[description appendString:@"\n"];
|
||||
for (GCDWebServerMultiPartFile* file in _files) {
|
||||
[description appendFormat:@"\n%@ (%@): %@\n{%@}", file.controlName, file.contentType, file.fileName, file.temporaryPath];
|
||||
}
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerDataRequest.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerURLEncodedFormRequest subclass of GCDWebServerRequest
|
||||
* parses the body of the HTTP request as a URL encoded form using
|
||||
* GCDWebServerParseURLEncodedForm().
|
||||
*/
|
||||
@interface GCDWebServerURLEncodedFormRequest : GCDWebServerDataRequest
|
||||
|
||||
/**
|
||||
* Returns the unescaped control names and values for the URL encoded form.
|
||||
*
|
||||
* The text encoding used to interpret the data is extracted from the
|
||||
* "Content-Type" header or defaults to UTF-8.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSDictionary* arguments;
|
||||
|
||||
/**
|
||||
* Returns the MIME type for URL encoded forms
|
||||
* i.e. "application/x-www-form-urlencoded".
|
||||
*/
|
||||
+ (NSString*)mimeType;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@implementation GCDWebServerURLEncodedFormRequest
|
||||
|
||||
+ (NSString*)mimeType {
|
||||
return @"application/x-www-form-urlencoded";
|
||||
}
|
||||
|
||||
- (BOOL)close:(NSError**)error {
|
||||
if (![super close:error]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset");
|
||||
NSString* string = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||
_arguments = GCDWebServerParseURLEncodedForm(string);
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithString:[super description]];
|
||||
[description appendString:@"\n"];
|
||||
for (NSString* argument in [[_arguments allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
|
||||
[description appendFormat:@"\n%@ = %@", argument, [_arguments objectForKey:argument]];
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerResponse.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerDataResponse subclass of GCDWebServerResponse reads the body
|
||||
* of the HTTP response from memory.
|
||||
*/
|
||||
@interface GCDWebServerDataResponse : GCDWebServerResponse
|
||||
@property(nonatomic, copy) NSString* contentType; // Redeclare as non-null
|
||||
|
||||
/**
|
||||
* Creates a response with data in memory and a given content type.
|
||||
*/
|
||||
+ (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type;
|
||||
|
||||
/**
|
||||
* This method is the designated initializer for the class.
|
||||
*/
|
||||
- (instancetype)initWithData:(NSData*)data contentType:(NSString*)type;
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerDataResponse (Extensions)
|
||||
|
||||
/**
|
||||
* Creates a data response from text encoded using UTF-8.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithText:(NSString*)text;
|
||||
|
||||
/**
|
||||
* Creates a data response from HTML encoded using UTF-8.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithHTML:(NSString*)html;
|
||||
|
||||
/**
|
||||
* Creates a data response from an HTML template encoded using UTF-8.
|
||||
* See -initWithHTMLTemplate:variables: for details.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables;
|
||||
|
||||
/**
|
||||
* Creates a data response from a serialized JSON object and the default
|
||||
* "application/json" content type.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithJSONObject:(id)object;
|
||||
|
||||
/**
|
||||
* Creates a data response from a serialized JSON object and a custom
|
||||
* content type.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type;
|
||||
|
||||
/**
|
||||
* Initializes a data response from text encoded using UTF-8.
|
||||
*/
|
||||
- (nullable instancetype)initWithText:(NSString*)text;
|
||||
|
||||
/**
|
||||
* Initializes a data response from HTML encoded using UTF-8.
|
||||
*/
|
||||
- (nullable instancetype)initWithHTML:(NSString*)html;
|
||||
|
||||
/**
|
||||
* Initializes a data response from an HTML template encoded using UTF-8.
|
||||
*
|
||||
* All occurences of "%variable%" within the HTML template are replaced with
|
||||
* their corresponding values.
|
||||
*/
|
||||
- (nullable instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables;
|
||||
|
||||
/**
|
||||
* Initializes a data response from a serialized JSON object and the default
|
||||
* "application/json" content type.
|
||||
*/
|
||||
- (nullable instancetype)initWithJSONObject:(id)object;
|
||||
|
||||
/**
|
||||
* Initializes a data response from a serialized JSON object and a custom
|
||||
* content type.
|
||||
*/
|
||||
- (nullable instancetype)initWithJSONObject:(id)object contentType:(NSString*)type;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@implementation GCDWebServerDataResponse {
|
||||
NSData* _data;
|
||||
BOOL _done;
|
||||
}
|
||||
|
||||
@dynamic contentType;
|
||||
|
||||
+ (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type {
|
||||
return [[[self class] alloc] initWithData:data contentType:type];
|
||||
}
|
||||
|
||||
- (instancetype)initWithData:(NSData*)data contentType:(NSString*)type {
|
||||
if ((self = [super init])) {
|
||||
_data = data;
|
||||
|
||||
self.contentType = type;
|
||||
self.contentLength = data.length;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
NSData* data;
|
||||
if (_done) {
|
||||
data = [NSData data];
|
||||
} else {
|
||||
data = _data;
|
||||
_done = YES;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithString:[super description]];
|
||||
[description appendString:@"\n\n"];
|
||||
[description appendString:GCDWebServerDescribeData(_data, self.contentType)];
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerDataResponse (Extensions)
|
||||
|
||||
+ (instancetype)responseWithText:(NSString*)text {
|
||||
return [[self alloc] initWithText:text];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithHTML:(NSString*)html {
|
||||
return [[self alloc] initWithHTML:html];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables {
|
||||
return [[self alloc] initWithHTMLTemplate:path variables:variables];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithJSONObject:(id)object {
|
||||
return [[self alloc] initWithJSONObject:object];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type {
|
||||
return [[self alloc] initWithJSONObject:object contentType:type];
|
||||
}
|
||||
|
||||
- (instancetype)initWithText:(NSString*)text {
|
||||
NSData* data = [text dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (data == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:@"text/plain; charset=utf-8"];
|
||||
}
|
||||
|
||||
- (instancetype)initWithHTML:(NSString*)html {
|
||||
NSData* data = [html dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (data == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:@"text/html; charset=utf-8"];
|
||||
}
|
||||
|
||||
- (instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables {
|
||||
NSMutableString* html = [[NSMutableString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
|
||||
[variables enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL* stop) {
|
||||
[html replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, html.length)];
|
||||
}];
|
||||
return [self initWithHTML:html];
|
||||
}
|
||||
|
||||
- (instancetype)initWithJSONObject:(id)object {
|
||||
return [self initWithJSONObject:object contentType:@"application/json"];
|
||||
}
|
||||
|
||||
- (instancetype)initWithJSONObject:(id)object contentType:(NSString*)type {
|
||||
NSData* data = [NSJSONSerialization dataWithJSONObject:object options:0 error:NULL];
|
||||
if (data == nil) {
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:type];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerDataResponse.h"
|
||||
#import "GCDWebServerHTTPStatusCodes.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerDataResponse subclass of GCDWebServerDataResponse generates
|
||||
* an HTML body from an HTTP status code and an error message.
|
||||
*/
|
||||
@interface GCDWebServerErrorResponse : GCDWebServerDataResponse
|
||||
|
||||
/**
|
||||
* Creates a client error response with the corresponding HTTP status code.
|
||||
*/
|
||||
+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3);
|
||||
|
||||
/**
|
||||
* Creates a server error response with the corresponding HTTP status code.
|
||||
*/
|
||||
+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3);
|
||||
|
||||
/**
|
||||
* Creates a client error response with the corresponding HTTP status code
|
||||
* and an underlying NSError.
|
||||
*/
|
||||
+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4);
|
||||
|
||||
/**
|
||||
* Creates a server error response with the corresponding HTTP status code
|
||||
* and an underlying NSError.
|
||||
*/
|
||||
+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4);
|
||||
|
||||
/**
|
||||
* Initializes a client error response with the corresponding HTTP status code.
|
||||
*/
|
||||
- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3);
|
||||
|
||||
/**
|
||||
* Initializes a server error response with the corresponding HTTP status code.
|
||||
*/
|
||||
- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3);
|
||||
|
||||
/**
|
||||
* Initializes a client error response with the corresponding HTTP status code
|
||||
* and an underlying NSError.
|
||||
*/
|
||||
- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4);
|
||||
|
||||
/**
|
||||
* Initializes a server error response with the corresponding HTTP status code
|
||||
* and an underlying NSError.
|
||||
*/
|
||||
- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(nullable NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4);
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@implementation GCDWebServerErrorResponse
|
||||
|
||||
+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return response;
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return response;
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return response;
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return response;
|
||||
}
|
||||
|
||||
static inline NSString* _EscapeHTMLString(NSString* string) {
|
||||
return [string stringByReplacingOccurrencesOfString:@"\"" withString:@"""];
|
||||
}
|
||||
|
||||
- (instancetype)initWithStatusCode:(NSInteger)statusCode underlyingError:(NSError*)underlyingError messageFormat:(NSString*)format arguments:(va_list)arguments {
|
||||
NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments];
|
||||
NSString* title = [NSString stringWithFormat:@"HTTP Error %i", (int)statusCode];
|
||||
NSString* error = underlyingError ? [NSString stringWithFormat:@"[%@] %@ (%li)", underlyingError.domain, _EscapeHTMLString(underlyingError.localizedDescription), (long)underlyingError.code] : @"";
|
||||
NSString* html = [NSString stringWithFormat:@"<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>%@</title></head><body><h1>%@: %@</h1><h3>%@</h3></body></html>",
|
||||
title, title, _EscapeHTMLString(message), error];
|
||||
if ((self = [self initWithHTML:html])) {
|
||||
self.statusCode = statusCode;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... {
|
||||
GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600));
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerResponse.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerFileResponse subclass of GCDWebServerResponse reads the body
|
||||
* of the HTTP response from a file on disk.
|
||||
*
|
||||
* It will automatically set the contentType, lastModifiedDate and eTag
|
||||
* properties of the GCDWebServerResponse according to the file extension and
|
||||
* metadata.
|
||||
*/
|
||||
@interface GCDWebServerFileResponse : GCDWebServerResponse
|
||||
@property(nonatomic, copy) NSString* contentType; // Redeclare as non-null
|
||||
@property(nonatomic) NSDate* lastModifiedDate; // Redeclare as non-null
|
||||
@property(nonatomic, copy) NSString* eTag; // Redeclare as non-null
|
||||
|
||||
/**
|
||||
* Creates a response with the contents of a file.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithFile:(NSString*)path;
|
||||
|
||||
/**
|
||||
* Creates a response like +responseWithFile: and sets the "Content-Disposition"
|
||||
* HTTP header for a download if the "attachment" argument is YES.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment;
|
||||
|
||||
/**
|
||||
* Creates a response like +responseWithFile: but restricts the file contents
|
||||
* to a specific byte range.
|
||||
*
|
||||
* See -initWithFile:byteRange: for details.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range;
|
||||
|
||||
/**
|
||||
* Creates a response like +responseWithFile:byteRange: and sets the
|
||||
* "Content-Disposition" HTTP header for a download if the "attachment"
|
||||
* argument is YES.
|
||||
*/
|
||||
+ (nullable instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment;
|
||||
|
||||
/**
|
||||
* Initializes a response with the contents of a file.
|
||||
*/
|
||||
- (nullable instancetype)initWithFile:(NSString*)path;
|
||||
|
||||
/**
|
||||
* Initializes a response like +responseWithFile: and sets the
|
||||
* "Content-Disposition" HTTP header for a download if the "attachment"
|
||||
* argument is YES.
|
||||
*/
|
||||
- (nullable instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment;
|
||||
|
||||
/**
|
||||
* Initializes a response like -initWithFile: but restricts the file contents
|
||||
* to a specific byte range. This range should be set to (NSUIntegerMax, 0) for
|
||||
* the full file, (offset, length) if expressed from the beginning of the file,
|
||||
* or (NSUIntegerMax, length) if expressed from the end of the file. The "offset"
|
||||
* and "length" values will be automatically adjusted to be compatible with the
|
||||
* actual size of the file.
|
||||
*
|
||||
* This argument would typically be set to the value of the byteRange property
|
||||
* of the current GCDWebServerRequest.
|
||||
*/
|
||||
- (nullable instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range;
|
||||
|
||||
/**
|
||||
* This method is the designated initializer for the class.
|
||||
*
|
||||
* If MIME type overrides are specified, they allow to customize the built-in
|
||||
* mapping from extensions to MIME types. Keys of the dictionary must be lowercased
|
||||
* file extensions without the period, and the values must be the corresponding
|
||||
* MIME types.
|
||||
*/
|
||||
- (nullable instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment mimeTypeOverrides:(nullable NSDictionary*)overrides;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import <sys/stat.h>
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
#define kFileReadBufferSize (32 * 1024)
|
||||
|
||||
@implementation GCDWebServerFileResponse {
|
||||
NSString* _path;
|
||||
NSUInteger _offset;
|
||||
NSUInteger _size;
|
||||
int _file;
|
||||
}
|
||||
|
||||
@dynamic contentType, lastModifiedDate, eTag;
|
||||
|
||||
+ (instancetype)responseWithFile:(NSString*)path {
|
||||
return [[[self class] alloc] initWithFile:path];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment {
|
||||
return [[[self class] alloc] initWithFile:path isAttachment:attachment];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range {
|
||||
return [[[self class] alloc] initWithFile:path byteRange:range];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment {
|
||||
return [[[self class] alloc] initWithFile:path byteRange:range isAttachment:attachment mimeTypeOverrides:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFile:(NSString*)path {
|
||||
return [self initWithFile:path byteRange:NSMakeRange(NSUIntegerMax, 0) isAttachment:NO mimeTypeOverrides:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment {
|
||||
return [self initWithFile:path byteRange:NSMakeRange(NSUIntegerMax, 0) isAttachment:attachment mimeTypeOverrides:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range {
|
||||
return [self initWithFile:path byteRange:range isAttachment:NO mimeTypeOverrides:nil];
|
||||
}
|
||||
|
||||
static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) {
|
||||
return [NSDate dateWithTimeIntervalSince1970:((NSTimeInterval)t->tv_sec + (NSTimeInterval)t->tv_nsec / 1000000000.0)];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment mimeTypeOverrides:(NSDictionary*)overrides {
|
||||
struct stat info;
|
||||
if (lstat([path fileSystemRepresentation], &info) || !(info.st_mode & S_IFREG)) {
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
#ifndef __LP64__
|
||||
if (info.st_size >= (off_t)4294967295) { // In 32 bit mode, we can't handle files greater than 4 GiBs (don't use "NSUIntegerMax" here to avoid potential unsigned to signed conversion issues)
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
#endif
|
||||
NSUInteger fileSize = (NSUInteger)info.st_size;
|
||||
|
||||
BOOL hasByteRange = GCDWebServerIsValidByteRange(range);
|
||||
if (hasByteRange) {
|
||||
if (range.location != NSUIntegerMax) {
|
||||
range.location = MIN(range.location, fileSize);
|
||||
range.length = MIN(range.length, fileSize - range.location);
|
||||
} else {
|
||||
range.length = MIN(range.length, fileSize);
|
||||
range.location = fileSize - range.length;
|
||||
}
|
||||
if (range.length == 0) {
|
||||
return nil; // TODO: Return 416 status code and "Content-Range: bytes */{file length}" header
|
||||
}
|
||||
} else {
|
||||
range.location = 0;
|
||||
range.length = fileSize;
|
||||
}
|
||||
|
||||
if ((self = [super init])) {
|
||||
_path = [path copy];
|
||||
_offset = range.location;
|
||||
_size = range.length;
|
||||
if (hasByteRange) {
|
||||
[self setStatusCode:kGCDWebServerHTTPStatusCode_PartialContent];
|
||||
[self setValue:[NSString stringWithFormat:@"bytes %lu-%lu/%lu", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), (unsigned long)fileSize] forAdditionalHeader:@"Content-Range"];
|
||||
GWS_LOG_DEBUG(@"Using content bytes range [%lu-%lu] for file \"%@\"", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), path);
|
||||
}
|
||||
|
||||
if (attachment) {
|
||||
NSString* fileName = [path lastPathComponent];
|
||||
NSData* data = [[fileName stringByReplacingOccurrencesOfString:@"\"" withString:@""] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES];
|
||||
NSString* lossyFileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil;
|
||||
if (lossyFileName) {
|
||||
NSString* value = [NSString stringWithFormat:@"attachment; filename=\"%@\"; filename*=UTF-8''%@", lossyFileName, GCDWebServerEscapeURLString(fileName)];
|
||||
[self setValue:value forAdditionalHeader:@"Content-Disposition"];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
self.contentType = GCDWebServerGetMimeTypeForExtension([_path pathExtension], overrides);
|
||||
self.contentLength = _size;
|
||||
self.lastModifiedDate = _NSDateFromTimeSpec(&info.st_mtimespec);
|
||||
self.eTag = [NSString stringWithFormat:@"%llu/%li/%li", info.st_ino, info.st_mtimespec.tv_sec, info.st_mtimespec.tv_nsec];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
_file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY);
|
||||
if (_file <= 0) {
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
if (lseek(_file, _offset, SEEK_SET) != (off_t)_offset) {
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
close(_file);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
size_t length = MIN((NSUInteger)kFileReadBufferSize, _size);
|
||||
NSMutableData* data = [[NSMutableData alloc] initWithLength:length];
|
||||
ssize_t result = read(_file, data.mutableBytes, length);
|
||||
if (result < 0) {
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
if (result > 0) {
|
||||
[data setLength:result];
|
||||
_size -= result;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
close(_file);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithString:[super description]];
|
||||
[description appendFormat:@"\n\n{%@}", _path];
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerResponse.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerStreamBlock is called to stream the data for the HTTP body.
|
||||
* The block must return either a chunk of data, an empty NSData when done, or
|
||||
* nil on error and set the "error" argument which is guaranteed to be non-NULL.
|
||||
*/
|
||||
typedef NSData* _Nullable (^GCDWebServerStreamBlock)(NSError** error);
|
||||
|
||||
/**
|
||||
* The GCDWebServerAsyncStreamBlock works like the GCDWebServerStreamBlock
|
||||
* except the streamed data can be returned at a later time allowing for
|
||||
* truly asynchronous generation of the data.
|
||||
*
|
||||
* The block must call "completionBlock" passing the new chunk of data when ready,
|
||||
* an empty NSData when done, or nil on error and pass a NSError.
|
||||
*
|
||||
* The block cannot call "completionBlock" more than once per invocation.
|
||||
*/
|
||||
typedef void (^GCDWebServerAsyncStreamBlock)(GCDWebServerBodyReaderCompletionBlock completionBlock);
|
||||
|
||||
/**
|
||||
* The GCDWebServerStreamedResponse subclass of GCDWebServerResponse streams
|
||||
* the body of the HTTP response using a GCD block.
|
||||
*/
|
||||
@interface GCDWebServerStreamedResponse : GCDWebServerResponse
|
||||
@property(nonatomic, copy) NSString* contentType; // Redeclare as non-null
|
||||
|
||||
/**
|
||||
* Creates a response with streamed data and a given content type.
|
||||
*/
|
||||
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block;
|
||||
|
||||
/**
|
||||
* Creates a response with async streamed data and a given content type.
|
||||
*/
|
||||
+ (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block;
|
||||
|
||||
/**
|
||||
* Initializes a response with streamed data and a given content type.
|
||||
*/
|
||||
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block;
|
||||
|
||||
/**
|
||||
* This method is the designated initializer for the class.
|
||||
*/
|
||||
- (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright (c) 2012-2015, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error GCDWebServer requires ARC
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@implementation GCDWebServerStreamedResponse {
|
||||
GCDWebServerAsyncStreamBlock _block;
|
||||
}
|
||||
|
||||
@dynamic contentType;
|
||||
|
||||
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
|
||||
return [[[self class] alloc] initWithContentType:type streamBlock:block];
|
||||
}
|
||||
|
||||
+ (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block {
|
||||
return [[[self class] alloc] initWithContentType:type asyncStreamBlock:block];
|
||||
}
|
||||
|
||||
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
|
||||
return [self initWithContentType:type
|
||||
asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) {
|
||||
|
||||
NSError* error = nil;
|
||||
NSData* data = block(&error);
|
||||
completionBlock(data, error);
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block {
|
||||
if ((self = [super init])) {
|
||||
_block = [block copy];
|
||||
|
||||
self.contentType = type;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block {
|
||||
_block(block);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithString:[super description]];
|
||||
[description appendString:@"\n\n<STREAM>"];
|
||||
return description;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,492 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
240A9FF92064AEAD008CBA94 /* LayaWKWebview.mm in Sources */ = {isa = PBXBuildFile; fileRef = 240A9FF82064AEAD008CBA94 /* LayaWKWebview.mm */; };
|
||||
240AA0242064C7B4008CBA94 /* CToObjectCIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0232064C7B4008CBA94 /* CToObjectCIOS.mm */; };
|
||||
240AA0272064DEE6008CBA94 /* conchConfig.mm in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0252064DEE5008CBA94 /* conchConfig.mm */; };
|
||||
240AA02E2068A380008CBA94 /* LayaCacheManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0212064C07B008CBA94 /* LayaCacheManager.cpp */; };
|
||||
240AA02F2068A380008CBA94 /* LayaCacheManager.h in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0222064C07B008CBA94 /* LayaCacheManager.h */; };
|
||||
240AA0302068A409008CBA94 /* GCDWebServerFunctions.h in Sources */ = {isa = PBXBuildFile; fileRef = 240A9FFC2064AF44008CBA94 /* GCDWebServerFunctions.h */; };
|
||||
240AA0312068A409008CBA94 /* GCDWebServerPrivate.h in Sources */ = {isa = PBXBuildFile; fileRef = 240A9FFD2064AF44008CBA94 /* GCDWebServerPrivate.h */; };
|
||||
240AA0322068A409008CBA94 /* GCDWebServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 240A9FFE2064AF44008CBA94 /* GCDWebServerResponse.m */; };
|
||||
240AA0332068A409008CBA94 /* GCDWebServerConnection.h in Sources */ = {isa = PBXBuildFile; fileRef = 240A9FFF2064AF44008CBA94 /* GCDWebServerConnection.h */; };
|
||||
240AA0342068A409008CBA94 /* GCDWebServer.h in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0002064AF44008CBA94 /* GCDWebServer.h */; };
|
||||
240AA0352068A409008CBA94 /* GCDWebServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0012064AF44008CBA94 /* GCDWebServerRequest.m */; };
|
||||
240AA0362068A409008CBA94 /* GCDWebServerHTTPStatusCodes.h in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0022064AF44008CBA94 /* GCDWebServerHTTPStatusCodes.h */; };
|
||||
240AA0372068A409008CBA94 /* GCDWebServerResponse.h in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0032064AF44008CBA94 /* GCDWebServerResponse.h */; };
|
||||
240AA0382068A409008CBA94 /* GCDWebServerFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0042064AF44008CBA94 /* GCDWebServerFunctions.m */; };
|
||||
240AA0392068A409008CBA94 /* GCDWebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0052064AF44008CBA94 /* GCDWebServer.m */; };
|
||||
240AA03A2068A409008CBA94 /* GCDWebServerConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0062064AF44008CBA94 /* GCDWebServerConnection.m */; };
|
||||
240AA03B2068A409008CBA94 /* GCDWebServerRequest.h in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0072064AF44008CBA94 /* GCDWebServerRequest.h */; };
|
||||
240AA03C2068A409008CBA94 /* GCDWebServerFileResponse.h in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0092064AF44008CBA94 /* GCDWebServerFileResponse.h */; };
|
||||
240AA03D2068A409008CBA94 /* GCDWebServerStreamedResponse.h in Sources */ = {isa = PBXBuildFile; fileRef = 240AA00A2064AF44008CBA94 /* GCDWebServerStreamedResponse.h */; };
|
||||
240AA03E2068A409008CBA94 /* GCDWebServerErrorResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 240AA00B2064AF44008CBA94 /* GCDWebServerErrorResponse.m */; };
|
||||
240AA03F2068A409008CBA94 /* GCDWebServerDataResponse.h in Sources */ = {isa = PBXBuildFile; fileRef = 240AA00C2064AF44008CBA94 /* GCDWebServerDataResponse.h */; };
|
||||
240AA0402068A409008CBA94 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 240AA00D2064AF44008CBA94 /* GCDWebServerFileResponse.m */; };
|
||||
240AA0412068A409008CBA94 /* GCDWebServerDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 240AA00E2064AF44008CBA94 /* GCDWebServerDataResponse.m */; };
|
||||
240AA0422068A409008CBA94 /* GCDWebServerErrorResponse.h in Sources */ = {isa = PBXBuildFile; fileRef = 240AA00F2064AF44008CBA94 /* GCDWebServerErrorResponse.h */; };
|
||||
240AA0432068A409008CBA94 /* GCDWebServerStreamedResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0102064AF44008CBA94 /* GCDWebServerStreamedResponse.m */; };
|
||||
240AA0442068A409008CBA94 /* GCDWebServerDataRequest.h in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0122064AF44008CBA94 /* GCDWebServerDataRequest.h */; };
|
||||
240AA0452068A409008CBA94 /* GCDWebServerMultiPartFormRequest.h in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0132064AF44008CBA94 /* GCDWebServerMultiPartFormRequest.h */; };
|
||||
240AA0462068A409008CBA94 /* GCDWebServerFileRequest.h in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0142064AF44008CBA94 /* GCDWebServerFileRequest.h */; };
|
||||
240AA0472068A409008CBA94 /* GCDWebServerURLEncodedFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0152064AF44008CBA94 /* GCDWebServerURLEncodedFormRequest.m */; };
|
||||
240AA0482068A409008CBA94 /* GCDWebServerMultiPartFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0162064AF44008CBA94 /* GCDWebServerMultiPartFormRequest.m */; };
|
||||
240AA0492068A409008CBA94 /* GCDWebServerDataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0172064AF44008CBA94 /* GCDWebServerDataRequest.m */; };
|
||||
240AA04A2068A409008CBA94 /* GCDWebServerFileRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0182064AF44008CBA94 /* GCDWebServerFileRequest.m */; };
|
||||
240AA04B2068A409008CBA94 /* GCDWebServerURLEncodedFormRequest.h in Sources */ = {isa = PBXBuildFile; fileRef = 240AA0192064AF44008CBA94 /* GCDWebServerURLEncodedFormRequest.h */; };
|
||||
2444415C22A0CE9700315BEA /* reflection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2444415B22A0CE9700315BEA /* reflection.mm */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
A2D1182E1D0BB25E004C229D /* Copy Files */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "include/$(PRODUCT_NAME)";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
);
|
||||
name = "Copy Files";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
240A9FF72064AEAD008CBA94 /* LayaWKWebview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LayaWKWebview.h; path = LayaWKWebview/LayaWKWebview.h; sourceTree = "<group>"; };
|
||||
240A9FF82064AEAD008CBA94 /* LayaWKWebview.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = LayaWKWebview.mm; path = LayaWKWebview/LayaWKWebview.mm; sourceTree = "<group>"; };
|
||||
240A9FFC2064AF44008CBA94 /* GCDWebServerFunctions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerFunctions.h; sourceTree = "<group>"; };
|
||||
240A9FFD2064AF44008CBA94 /* GCDWebServerPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerPrivate.h; sourceTree = "<group>"; };
|
||||
240A9FFE2064AF44008CBA94 /* GCDWebServerResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerResponse.m; sourceTree = "<group>"; };
|
||||
240A9FFF2064AF44008CBA94 /* GCDWebServerConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerConnection.h; sourceTree = "<group>"; };
|
||||
240AA0002064AF44008CBA94 /* GCDWebServer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServer.h; sourceTree = "<group>"; };
|
||||
240AA0012064AF44008CBA94 /* GCDWebServerRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerRequest.m; sourceTree = "<group>"; };
|
||||
240AA0022064AF44008CBA94 /* GCDWebServerHTTPStatusCodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerHTTPStatusCodes.h; sourceTree = "<group>"; };
|
||||
240AA0032064AF44008CBA94 /* GCDWebServerResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerResponse.h; sourceTree = "<group>"; };
|
||||
240AA0042064AF44008CBA94 /* GCDWebServerFunctions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerFunctions.m; sourceTree = "<group>"; };
|
||||
240AA0052064AF44008CBA94 /* GCDWebServer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDWebServer.m; sourceTree = "<group>"; };
|
||||
240AA0062064AF44008CBA94 /* GCDWebServerConnection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerConnection.m; sourceTree = "<group>"; };
|
||||
240AA0072064AF44008CBA94 /* GCDWebServerRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerRequest.h; sourceTree = "<group>"; };
|
||||
240AA0092064AF44008CBA94 /* GCDWebServerFileResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerFileResponse.h; sourceTree = "<group>"; };
|
||||
240AA00A2064AF44008CBA94 /* GCDWebServerStreamedResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerStreamedResponse.h; sourceTree = "<group>"; };
|
||||
240AA00B2064AF44008CBA94 /* GCDWebServerErrorResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerErrorResponse.m; sourceTree = "<group>"; };
|
||||
240AA00C2064AF44008CBA94 /* GCDWebServerDataResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerDataResponse.h; sourceTree = "<group>"; };
|
||||
240AA00D2064AF44008CBA94 /* GCDWebServerFileResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerFileResponse.m; sourceTree = "<group>"; };
|
||||
240AA00E2064AF44008CBA94 /* GCDWebServerDataResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerDataResponse.m; sourceTree = "<group>"; };
|
||||
240AA00F2064AF44008CBA94 /* GCDWebServerErrorResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerErrorResponse.h; sourceTree = "<group>"; };
|
||||
240AA0102064AF44008CBA94 /* GCDWebServerStreamedResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerStreamedResponse.m; sourceTree = "<group>"; };
|
||||
240AA0122064AF44008CBA94 /* GCDWebServerDataRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerDataRequest.h; sourceTree = "<group>"; };
|
||||
240AA0132064AF44008CBA94 /* GCDWebServerMultiPartFormRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerMultiPartFormRequest.h; sourceTree = "<group>"; };
|
||||
240AA0142064AF44008CBA94 /* GCDWebServerFileRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerFileRequest.h; sourceTree = "<group>"; };
|
||||
240AA0152064AF44008CBA94 /* GCDWebServerURLEncodedFormRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerURLEncodedFormRequest.m; sourceTree = "<group>"; };
|
||||
240AA0162064AF44008CBA94 /* GCDWebServerMultiPartFormRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerMultiPartFormRequest.m; sourceTree = "<group>"; };
|
||||
240AA0172064AF44008CBA94 /* GCDWebServerDataRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerDataRequest.m; sourceTree = "<group>"; };
|
||||
240AA0182064AF44008CBA94 /* GCDWebServerFileRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerFileRequest.m; sourceTree = "<group>"; };
|
||||
240AA0192064AF44008CBA94 /* GCDWebServerURLEncodedFormRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDWebServerURLEncodedFormRequest.h; sourceTree = "<group>"; };
|
||||
240AA01B2064B072008CBA94 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
|
||||
240AA0212064C07B008CBA94 /* LayaCacheManager.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LayaCacheManager.cpp; sourceTree = "<group>"; };
|
||||
240AA0222064C07B008CBA94 /* LayaCacheManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LayaCacheManager.h; sourceTree = "<group>"; };
|
||||
240AA0232064C7B4008CBA94 /* CToObjectCIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CToObjectCIOS.mm; path = LayaWKWebview/CToObjectCIOS.mm; sourceTree = "<group>"; };
|
||||
240AA0252064DEE5008CBA94 /* conchConfig.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = conchConfig.mm; path = LayaWKWebview/conchConfig.mm; sourceTree = "<group>"; };
|
||||
240AA0262064DEE6008CBA94 /* conchConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = conchConfig.h; path = LayaWKWebview/conchConfig.h; sourceTree = "<group>"; };
|
||||
2444415A22A0CE9700315BEA /* refection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = refection.h; path = LayaWKWebview/refection.h; sourceTree = "<group>"; };
|
||||
2444415B22A0CE9700315BEA /* reflection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = reflection.mm; path = LayaWKWebview/reflection.mm; sourceTree = "<group>"; };
|
||||
A2D118301D0BB25E004C229D /* librender.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = librender.a; path = libLayaWKWebview.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
A2D1182D1D0BB25E004C229D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
240A9FFA2064AF44008CBA94 /* GCDWebServer */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
240A9FFB2064AF44008CBA94 /* Core */,
|
||||
240AA0082064AF44008CBA94 /* Responses */,
|
||||
240AA0112064AF44008CBA94 /* Requests */,
|
||||
);
|
||||
path = GCDWebServer;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
240A9FFB2064AF44008CBA94 /* Core */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
240A9FFC2064AF44008CBA94 /* GCDWebServerFunctions.h */,
|
||||
240A9FFD2064AF44008CBA94 /* GCDWebServerPrivate.h */,
|
||||
240A9FFE2064AF44008CBA94 /* GCDWebServerResponse.m */,
|
||||
240A9FFF2064AF44008CBA94 /* GCDWebServerConnection.h */,
|
||||
240AA0002064AF44008CBA94 /* GCDWebServer.h */,
|
||||
240AA0012064AF44008CBA94 /* GCDWebServerRequest.m */,
|
||||
240AA0022064AF44008CBA94 /* GCDWebServerHTTPStatusCodes.h */,
|
||||
240AA0032064AF44008CBA94 /* GCDWebServerResponse.h */,
|
||||
240AA0042064AF44008CBA94 /* GCDWebServerFunctions.m */,
|
||||
240AA0052064AF44008CBA94 /* GCDWebServer.m */,
|
||||
240AA0062064AF44008CBA94 /* GCDWebServerConnection.m */,
|
||||
240AA0072064AF44008CBA94 /* GCDWebServerRequest.h */,
|
||||
);
|
||||
path = Core;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
240AA0082064AF44008CBA94 /* Responses */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
240AA0092064AF44008CBA94 /* GCDWebServerFileResponse.h */,
|
||||
240AA00A2064AF44008CBA94 /* GCDWebServerStreamedResponse.h */,
|
||||
240AA00B2064AF44008CBA94 /* GCDWebServerErrorResponse.m */,
|
||||
240AA00C2064AF44008CBA94 /* GCDWebServerDataResponse.h */,
|
||||
240AA00D2064AF44008CBA94 /* GCDWebServerFileResponse.m */,
|
||||
240AA00E2064AF44008CBA94 /* GCDWebServerDataResponse.m */,
|
||||
240AA00F2064AF44008CBA94 /* GCDWebServerErrorResponse.h */,
|
||||
240AA0102064AF44008CBA94 /* GCDWebServerStreamedResponse.m */,
|
||||
);
|
||||
path = Responses;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
240AA0112064AF44008CBA94 /* Requests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
240AA0122064AF44008CBA94 /* GCDWebServerDataRequest.h */,
|
||||
240AA0132064AF44008CBA94 /* GCDWebServerMultiPartFormRequest.h */,
|
||||
240AA0142064AF44008CBA94 /* GCDWebServerFileRequest.h */,
|
||||
240AA0152064AF44008CBA94 /* GCDWebServerURLEncodedFormRequest.m */,
|
||||
240AA0162064AF44008CBA94 /* GCDWebServerMultiPartFormRequest.m */,
|
||||
240AA0172064AF44008CBA94 /* GCDWebServerDataRequest.m */,
|
||||
240AA0182064AF44008CBA94 /* GCDWebServerFileRequest.m */,
|
||||
240AA0192064AF44008CBA94 /* GCDWebServerURLEncodedFormRequest.h */,
|
||||
);
|
||||
path = Requests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
240AA01A2064B072008CBA94 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
240AA01B2064B072008CBA94 /* WebKit.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
240AA0202064C07B008CBA94 /* LayaCache */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
240AA0212064C07B008CBA94 /* LayaCacheManager.cpp */,
|
||||
240AA0222064C07B008CBA94 /* LayaCacheManager.h */,
|
||||
);
|
||||
name = LayaCache;
|
||||
path = LayaWKWebview/LayaCache;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A2D118271D0BB25E004C229D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A2D1183C1D0BB442004C229D /* Source */,
|
||||
A2D118311D0BB25E004C229D /* Products */,
|
||||
240AA01A2064B072008CBA94 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A2D118311D0BB25E004C229D /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A2D118301D0BB25E004C229D /* librender.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A2D1183C1D0BB442004C229D /* Source */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2444415A22A0CE9700315BEA /* refection.h */,
|
||||
2444415B22A0CE9700315BEA /* reflection.mm */,
|
||||
240AA0262064DEE6008CBA94 /* conchConfig.h */,
|
||||
240AA0252064DEE5008CBA94 /* conchConfig.mm */,
|
||||
240AA0232064C7B4008CBA94 /* CToObjectCIOS.mm */,
|
||||
240AA0202064C07B008CBA94 /* LayaCache */,
|
||||
240A9FFA2064AF44008CBA94 /* GCDWebServer */,
|
||||
240A9FF72064AEAD008CBA94 /* LayaWKWebview.h */,
|
||||
240A9FF82064AEAD008CBA94 /* LayaWKWebview.mm */,
|
||||
);
|
||||
name = Source;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
A2D1182F1D0BB25E004C229D /* LayaWKWebview */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = A2D118391D0BB25E004C229D /* Build configuration list for PBXNativeTarget "LayaWKWebview" */;
|
||||
buildPhases = (
|
||||
A2D1182C1D0BB25E004C229D /* Sources */,
|
||||
A2D1182D1D0BB25E004C229D /* Frameworks */,
|
||||
A2D1182E1D0BB25E004C229D /* Copy Files */,
|
||||
5E4A02C41F54353800926BA4 /* ShellScript */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = LayaWKWebview;
|
||||
productName = render;
|
||||
productReference = A2D118301D0BB25E004C229D /* librender.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
A2D118281D0BB25E004C229D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0920;
|
||||
ORGANIZATIONNAME = render;
|
||||
TargetAttributes = {
|
||||
A2D1182F1D0BB25E004C229D = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = A2D1182B1D0BB25E004C229D /* Build configuration list for PBXProject "LayaWKWebview" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
English,
|
||||
en,
|
||||
);
|
||||
mainGroup = A2D118271D0BB25E004C229D;
|
||||
productRefGroup = A2D118311D0BB25E004C229D /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
A2D1182F1D0BB25E004C229D /* LayaWKWebview */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
5E4A02C41F54353800926BA4 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "chmod u+x $SRCROOT/../../copyLib.sh\n$SRCROOT/../../copyLib.sh";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
A2D1182C1D0BB25E004C229D /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
240AA0302068A409008CBA94 /* GCDWebServerFunctions.h in Sources */,
|
||||
240AA0312068A409008CBA94 /* GCDWebServerPrivate.h in Sources */,
|
||||
240AA0322068A409008CBA94 /* GCDWebServerResponse.m in Sources */,
|
||||
240AA0332068A409008CBA94 /* GCDWebServerConnection.h in Sources */,
|
||||
240AA0342068A409008CBA94 /* GCDWebServer.h in Sources */,
|
||||
240AA0352068A409008CBA94 /* GCDWebServerRequest.m in Sources */,
|
||||
240AA0362068A409008CBA94 /* GCDWebServerHTTPStatusCodes.h in Sources */,
|
||||
240AA0372068A409008CBA94 /* GCDWebServerResponse.h in Sources */,
|
||||
240AA0382068A409008CBA94 /* GCDWebServerFunctions.m in Sources */,
|
||||
240AA0392068A409008CBA94 /* GCDWebServer.m in Sources */,
|
||||
240AA03A2068A409008CBA94 /* GCDWebServerConnection.m in Sources */,
|
||||
240AA03B2068A409008CBA94 /* GCDWebServerRequest.h in Sources */,
|
||||
2444415C22A0CE9700315BEA /* reflection.mm in Sources */,
|
||||
240AA03C2068A409008CBA94 /* GCDWebServerFileResponse.h in Sources */,
|
||||
240AA03D2068A409008CBA94 /* GCDWebServerStreamedResponse.h in Sources */,
|
||||
240AA03E2068A409008CBA94 /* GCDWebServerErrorResponse.m in Sources */,
|
||||
240AA03F2068A409008CBA94 /* GCDWebServerDataResponse.h in Sources */,
|
||||
240AA0402068A409008CBA94 /* GCDWebServerFileResponse.m in Sources */,
|
||||
240AA0412068A409008CBA94 /* GCDWebServerDataResponse.m in Sources */,
|
||||
240AA0422068A409008CBA94 /* GCDWebServerErrorResponse.h in Sources */,
|
||||
240AA0432068A409008CBA94 /* GCDWebServerStreamedResponse.m in Sources */,
|
||||
240AA0442068A409008CBA94 /* GCDWebServerDataRequest.h in Sources */,
|
||||
240AA0452068A409008CBA94 /* GCDWebServerMultiPartFormRequest.h in Sources */,
|
||||
240AA0462068A409008CBA94 /* GCDWebServerFileRequest.h in Sources */,
|
||||
240AA0472068A409008CBA94 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
|
||||
240AA0482068A409008CBA94 /* GCDWebServerMultiPartFormRequest.m in Sources */,
|
||||
240AA0492068A409008CBA94 /* GCDWebServerDataRequest.m in Sources */,
|
||||
240AA04A2068A409008CBA94 /* GCDWebServerFileRequest.m in Sources */,
|
||||
240AA04B2068A409008CBA94 /* GCDWebServerURLEncodedFormRequest.h in Sources */,
|
||||
240AA02E2068A380008CBA94 /* LayaCacheManager.cpp in Sources */,
|
||||
240AA02F2068A380008CBA94 /* LayaCacheManager.h in Sources */,
|
||||
240A9FF92064AEAD008CBA94 /* LayaWKWebview.mm in Sources */,
|
||||
240AA0242064C7B4008CBA94 /* CToObjectCIOS.mm in Sources */,
|
||||
240AA0272064DEE6008CBA94 /* conchConfig.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
A2D118371D0BB25E004C229D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
A2D118381D0BB25E004C229D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
A2D1183A1D0BB25E004C229D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
LIBRARY_SEARCH_PATHS = "";
|
||||
OTHER_CFLAGS = (
|
||||
"-I../../../include/common",
|
||||
"-I../../../ThirdParty/curl/include/ios",
|
||||
"-I../../../ThirdParty/openssl/include/ios",
|
||||
);
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
VALID_ARCHS = "arm64 armv7 i386 x86_64";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
A2D1183B1D0BB25E004C229D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
LIBRARY_SEARCH_PATHS = "";
|
||||
OTHER_CFLAGS = (
|
||||
"-I../../../include/common",
|
||||
"-I../../../ThirdParty/curl/include/ios",
|
||||
"-I../../../ThirdParty/openssl/include/ios",
|
||||
);
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
VALID_ARCHS = "arm64 armv7 i386 x86_64";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
A2D1182B1D0BB25E004C229D /* Build configuration list for PBXProject "LayaWKWebview" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
A2D118371D0BB25E004C229D /* Debug */,
|
||||
A2D118381D0BB25E004C229D /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
A2D118391D0BB25E004C229D /* Build configuration list for PBXNativeTarget "LayaWKWebview" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
A2D1183A1D0BB25E004C229D /* Debug */,
|
||||
A2D1183B1D0BB25E004C229D /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = A2D118281D0BB25E004C229D /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,54 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
//文字相关的函数
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
void CToObjectCLog( const char* szFormat,...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, szFormat);
|
||||
NSString* nsFormat = [NSString stringWithUTF8String:szFormat];
|
||||
NSLogv( nsFormat, args);
|
||||
va_end(args);
|
||||
}
|
||||
void CToObjectCLogI( const char* szFormat,...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, szFormat);
|
||||
NSString* nsFormat = [NSString stringWithUTF8String:szFormat];
|
||||
NSLogv( nsFormat, args);
|
||||
va_end(args);
|
||||
}
|
||||
void CToObjectCLogE( const char* szFormat,...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, szFormat);
|
||||
NSString* nsFormat = [NSString stringWithUTF8String:szFormat];
|
||||
NSLogv( nsFormat, args);
|
||||
va_end(args);
|
||||
}
|
||||
void CToObjectCLogW( const char* szFormat,...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, szFormat);
|
||||
NSString* nsFormat = [NSString stringWithUTF8String:szFormat];
|
||||
NSLogv( nsFormat, args);
|
||||
va_end(args);
|
||||
}
|
||||
void alert(const char* fmt, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
char* pBuf = NULL;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int len = vsprintf(buf, fmt, args);
|
||||
if (len < 0) {
|
||||
pBuf = new char[4096];
|
||||
len = vsprintf(pBuf, fmt, args);
|
||||
}
|
||||
va_end(args);
|
||||
CToObjectCLog(pBuf ? pBuf : buf);
|
||||
if (pBuf)
|
||||
{
|
||||
delete[] pBuf;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
|
||||
|
||||
#include "LayaCacheManager.h"
|
||||
#include "resource/JCFileResManager.h"
|
||||
#include "downloadCache/JCServerFileCache.h"
|
||||
#include "downloadMgr/JCDownloadMgr.h"
|
||||
#include "misc/JCWorkerThread.h"
|
||||
#include <cmath>
|
||||
std::string gAssetRootPath = "";
|
||||
std::string gRedistPath = "";
|
||||
namespace laya {
|
||||
|
||||
LayaCacheManager* LayaCacheManager::m_pInstance = nullptr;
|
||||
|
||||
LayaCacheManager::LayaCacheManager()
|
||||
{
|
||||
m_pSvFileCache = nullptr;
|
||||
pthread_key_create(&JCWorkerThread::s_tls_curThread, NULL);
|
||||
pthread_key_create(&s_tls_curDataThread, NULL);
|
||||
JCDownloadMgr::getInstance()->init(3);//多加一个线程,可能要给优先级较低的任务用。
|
||||
m_pFileResMgr = new JCFileResManager(JCDownloadMgr::getInstance());
|
||||
|
||||
m_CallbackRef.reset(new int(1));
|
||||
}
|
||||
|
||||
LayaCacheManager::~LayaCacheManager()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
LayaCacheManager* LayaCacheManager::getInstance(){
|
||||
if( m_pInstance == NULL){
|
||||
m_pInstance = new LayaCacheManager();
|
||||
}
|
||||
return m_pInstance;
|
||||
}
|
||||
|
||||
void LayaCacheManager::delInstance(){
|
||||
if(m_pInstance){
|
||||
delete m_pInstance;
|
||||
m_pInstance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
std::string LayaCacheManager::preUpdateDcc(const std::string& redistPath,const std::string& domain)
|
||||
{
|
||||
if (m_pSvFileCache != nullptr){
|
||||
delete m_pSvFileCache;
|
||||
}
|
||||
m_pSvFileCache = new laya::JCServerFileCache();
|
||||
m_pFileResMgr->setFileCache(m_pSvFileCache);
|
||||
m_pSvFileCache->setCachePath((redistPath + "appCache").c_str());
|
||||
m_pSvFileCache->switchToApp(domain.c_str());
|
||||
|
||||
m_pSvFileCache->setResourceID("appurl", domain.c_str());
|
||||
return m_pSvFileCache->getResourceID("netassetsid");
|
||||
}
|
||||
|
||||
void LayaCacheManager::updateDccClearAssetsid(const std::string& redistPath,const std::string& domain)
|
||||
{
|
||||
m_pSvFileCache->saveFileTable("");
|
||||
m_pSvFileCache->setResourceID("netassetsid", "");
|
||||
if (m_pSvFileCache != nullptr){
|
||||
delete m_pSvFileCache;
|
||||
}
|
||||
m_pSvFileCache = new laya::JCServerFileCache();
|
||||
m_pFileResMgr->setFileCache(m_pSvFileCache);
|
||||
m_pSvFileCache->setCachePath((redistPath + "appCache").c_str());
|
||||
m_pSvFileCache->switchToApp(domain.c_str());
|
||||
}
|
||||
|
||||
void LayaCacheManager::doUpdateDcc(const std::string& redistPath,const std::string& domain,const std::string& txtdcc,const std::string& assetsid)
|
||||
{
|
||||
m_pSvFileCache->saveFileTable(txtdcc.c_str());
|
||||
if (m_pSvFileCache != nullptr){
|
||||
delete m_pSvFileCache;
|
||||
}
|
||||
m_pSvFileCache = new laya::JCServerFileCache();
|
||||
m_pFileResMgr->setFileCache(m_pSvFileCache);
|
||||
m_pSvFileCache->setCachePath((redistPath + "appCache").c_str());
|
||||
m_pSvFileCache->switchToApp(domain.c_str());
|
||||
|
||||
m_pSvFileCache->setResourceID("netassetsid", assetsid.c_str());
|
||||
}
|
||||
void LayaCacheManager::handleRequest(const char* strUrl, std::function<void(void* pData, int length)> onDownload, std::function<void(int errCode)> onError)
|
||||
{
|
||||
laya::JCFileRes* pRes = m_pFileResMgr->getRes(strUrl);
|
||||
std::weak_ptr<int> cbref(m_CallbackRef);
|
||||
pRes->setOnReadyCB(std::bind(&LayaCacheManager::onDownloadEnd,this, std::placeholders::_1, cbref, onDownload));
|
||||
pRes->setOnErrorCB(std::bind(&LayaCacheManager::onDownloadErr, this, std::placeholders::_1, std::placeholders::_2, cbref, onError));
|
||||
|
||||
}
|
||||
bool LayaCacheManager::onDownloadErr(void* p_pRes, int p_nErrCode,std::weak_ptr<int> callbackref, std::function<void(int errCode)> onError)
|
||||
{
|
||||
if (!callbackref.lock())
|
||||
return false;
|
||||
onError(p_nErrCode);
|
||||
return true;
|
||||
}
|
||||
bool LayaCacheManager::onDownloadEnd(void* p_pRes, std::weak_ptr<int> callbackref, std::function<void(void* pData, int length)> onDownload)
|
||||
{
|
||||
if( !callbackref.lock() )
|
||||
return false;
|
||||
|
||||
laya::JCResStateDispatcher* pRes = (laya::JCResStateDispatcher*)p_pRes;
|
||||
laya::JCFileRes* pFileRes = (laya::JCFileRes*)pRes;
|
||||
|
||||
if( pFileRes->m_pBuffer.get()==NULL || pFileRes->m_nLength==0 ){
|
||||
return false;
|
||||
}
|
||||
|
||||
onDownload(pFileRes->m_pBuffer.get(), pFileRes->m_nLength);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
#ifndef LayaCacheManager_hpp
|
||||
#define LayaCacheManager_hpp
|
||||
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <functional>
|
||||
|
||||
namespace laya {
|
||||
|
||||
class JCFileResManager;
|
||||
class JCServerFileCache;
|
||||
class JCFileRes;
|
||||
class LayaCacheManager
|
||||
{
|
||||
public:
|
||||
LayaCacheManager();
|
||||
virtual ~LayaCacheManager();
|
||||
static LayaCacheManager* getInstance();
|
||||
static void delInstance();
|
||||
void handleRequest(const char* strUrl, std::function<void(void* pData, int length)> onDownload, std::function<void(int errCode)> onError);
|
||||
bool onDownloadErr(void* p_pRes, int p_nErrCode,std::weak_ptr<int> callbackref, std::function<void(int errCode)> onError);
|
||||
bool onDownloadEnd(void* p_pRes, std::weak_ptr<int> callbackref, std::function<void(void* pData, int length)> onDownload);
|
||||
std::string preUpdateDcc(const std::string& redistPath,const std::string& domain);
|
||||
void updateDccClearAssetsid(const std::string& redistPath,const std::string& domain);
|
||||
void doUpdateDcc(const std::string& redistPath,const std::string& domain,const std::string& txtdcc,const std::string& assetsid);
|
||||
protected:
|
||||
JCFileResManager* m_pFileResMgr;
|
||||
JCServerFileCache* m_pSvFileCache;
|
||||
static LayaCacheManager* m_pInstance;
|
||||
std::shared_ptr<int> m_CallbackRef;
|
||||
};
|
||||
}
|
||||
#endif /* LayaCacheManager_hpp */
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// LayaWKWebview.h
|
||||
// LayaWKWebview
|
||||
//
|
||||
// Created by helloworldlv on 2018/3/23.
|
||||
// Copyright © 2018年 render. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
#import <WebKit/WKWebview.h>
|
||||
#import <WebKit/WKUserContentController.h>
|
||||
#import <WebKit/WKUserScript.h>
|
||||
|
||||
@interface LayaWKWebview : NSObject
|
||||
@property(nonatomic, strong) WKWebView* webview;
|
||||
/*接管下载,支持DCC*/
|
||||
- (instancetype)initWithWebview:(WKWebView*)webview url:(NSString*)url hostPort:(int)port;
|
||||
/*直接调WKWebview,不支持DCC*/
|
||||
- (instancetype)initWithWebview:(WKWebView*)webview url:(NSString*)url;
|
||||
- (void)loadUrl:(NSString*)url;
|
||||
- (NSString*)callMethod:(int)objid className:(NSString*)cls methodName:(NSString*)method param:(NSString*)param;
|
||||
-(void)callbackToJSWithClass:(Class)cls methodName:(NSString*)name ret:(NSObject*)retObj;
|
||||
-(void)callbackToJSWithClassName:(NSString*)cls methodName:(NSString*)name ret:(NSObject*)retObj;
|
||||
-(void)callbackToJSWithObject:(id)obj methodName:(NSString*)name ret:(NSObject*)retObj;
|
||||
-(void)runJS:(NSString*)script;
|
||||
+(LayaWKWebview*)GetLayaWKWebview;
|
||||
@end
|
||||
@@ -0,0 +1,325 @@
|
||||
//
|
||||
// LayaWKWebview.m
|
||||
// LayaWKWebview
|
||||
//
|
||||
// Created by helloworldlv on 2018/3/23.
|
||||
// Copyright © 2018年 render. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LayaWKWebview.h"
|
||||
#import "GCDWebServer.h"
|
||||
#import "GCDWebServerDataResponse.h"
|
||||
#import "GCDWebServerFunctions.h"
|
||||
#import "refection.h"
|
||||
#include <string>
|
||||
#include "LayaCache/LayaCacheManager.h"
|
||||
extern std::string gRedistPath;
|
||||
extern std::string gAssetRootPath;
|
||||
|
||||
static LayaWKWebview* g_pLayaWKWebview = nil;
|
||||
|
||||
@implementation LayaWKWebview
|
||||
{
|
||||
NSString* _url;
|
||||
int _hostPort;
|
||||
GCDWebServer* _GCDWebServer;
|
||||
Reflection* _reflection;
|
||||
NSString* _urlSuffix;
|
||||
}
|
||||
+(LayaWKWebview*)GetLayaWKWebview
|
||||
{
|
||||
return g_pLayaWKWebview;
|
||||
}
|
||||
- (instancetype)initWithWebview:(WKWebView*)webview url:(NSString*)url
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
g_pLayaWKWebview = self;
|
||||
_webview = webview;
|
||||
_url = url;
|
||||
_hostPort = 0;
|
||||
NSURL* pUrl = [NSURL URLWithString:url];
|
||||
NSURLRequest* pUrlRequest = [NSURLRequest requestWithURL:pUrl];
|
||||
[_webview loadRequest:pUrlRequest];
|
||||
_reflection = [[Reflection alloc]init:self];
|
||||
_urlSuffix = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (instancetype)initWithWebview:(WKWebView*)webview url:(NSString*)url hostPort:(int)port;
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
g_pLayaWKWebview = self;
|
||||
_reflection = [[Reflection alloc]init:self];
|
||||
_webview = webview;
|
||||
_url = url;
|
||||
_hostPort = port;
|
||||
if ([url isEqual:@"http://stand.alone.version/index.html"] || [url isEqual:@"http://stand.alone.version/index.js"])
|
||||
{
|
||||
gRedistPath = [[self getRootCachePath] cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
gAssetRootPath= [[self getResourcePath] cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
gAssetRootPath += "/cache/";
|
||||
NSString* urlpath = [self getLaunchUrlDomain:url];
|
||||
NSLog(@"[dcc] [%@]",url);
|
||||
laya::LayaCacheManager::getInstance()->preUpdateDcc(gRedistPath,urlpath.UTF8String);
|
||||
[self requestGame:url];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self updateDcc:_url];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
-(NSString *)convertDataToHexStr:(NSData *)data
|
||||
{
|
||||
if (!data || [data length] == 0)
|
||||
{
|
||||
return @"";
|
||||
}
|
||||
NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[data length]];
|
||||
[data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop)
|
||||
{
|
||||
unsigned char *dataBytes = (unsigned char*)bytes;
|
||||
for (NSInteger i = byteRange.length - 1; i >=0 ; i--)
|
||||
{
|
||||
NSString *hexStr = [NSString stringWithFormat:@"%x", (dataBytes[i]) & 0xff];
|
||||
if ([hexStr length] == 2)
|
||||
{
|
||||
[string appendString:hexStr];
|
||||
} else
|
||||
{
|
||||
[string appendFormat:@"0%@", hexStr];
|
||||
}
|
||||
}
|
||||
}];
|
||||
return string;
|
||||
}
|
||||
-(NSString*)getLaunchUrlDomain:(NSString*)url
|
||||
{
|
||||
NSString* domain = [[NSString alloc] initWithString:url];
|
||||
domain = [domain stringByReplacingOccurrencesOfString:@"?" withString:@""];
|
||||
domain = [[NSURL alloc] initWithString:domain].URLByDeletingLastPathComponent.absoluteString;
|
||||
return domain;
|
||||
}
|
||||
-(NSString*)getLayaBoxUrl
|
||||
{
|
||||
return [NSString stringWithFormat:@"http://127.0.0.1:%lu/s/%@", (unsigned long)(_GCDWebServer.port),_url];
|
||||
}
|
||||
-(NSString*) getResourcePath
|
||||
{
|
||||
return [[NSBundle mainBundle] resourcePath];
|
||||
}
|
||||
-(NSString*) getRootCachePath
|
||||
{
|
||||
NSString* sAppDirctory = NSHomeDirectory();
|
||||
NSString* sDownloadRootPath = [ NSString stringWithFormat: @"%@/Library/Caches/", sAppDirctory ];
|
||||
return sDownloadRootPath;
|
||||
}
|
||||
- (void)updateDcc:(NSString*)url
|
||||
{
|
||||
gRedistPath = [[self getRootCachePath] cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
gAssetRootPath= [[self getResourcePath] cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
gAssetRootPath += "/cache/";
|
||||
NSLog(@"[dcc] update dcc start [%@] ...",url);
|
||||
NSString* urlpath = [self getLaunchUrlDomain:url];
|
||||
/*NSString* urlpath = [[NSString alloc] initWithString:url];
|
||||
urlpath = [urlpath stringByReplacingOccurrencesOfString:@"?" withString:@""];
|
||||
urlpath = [[NSURL alloc] initWithString:urlpath].URLByDeletingLastPathComponent.absoluteString;*/
|
||||
NSLog(@"[dcc] [%@]",url);
|
||||
std::string curassets = laya::LayaCacheManager::getInstance()->preUpdateDcc(gRedistPath,urlpath.UTF8String);
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
|
||||
NSString* asidUrl = [[NSString alloc] initWithFormat:@"%@update/assetsid.txt?rand=%f",urlpath,(rand()/(float)RAND_MAX) * [NSDate date].timeIntervalSince1970];
|
||||
NSLog(@"[dcc] %@",asidUrl);
|
||||
|
||||
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:asidUrl] completionHandler:^(NSData * data, NSURLResponse * response, NSError * error)
|
||||
{
|
||||
NSLog(@"[dcc] assetsid.txt download complete");
|
||||
NSString* assetsidStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
if (error || (assetsidStr == nil) || [assetsidStr isEqualToString:@""])
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
NSLog(@"[dcc] assetsid.txt download error [%@] [%@] [%@]",error.localizedFailureReason,error.localizedDescription,error.localizedRecoverySuggestion);
|
||||
}
|
||||
laya::LayaCacheManager::getInstance()->updateDccClearAssetsid(gRedistPath,urlpath.UTF8String);
|
||||
[self requestGame:url];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([assetsidStr UTF8String] != curassets)
|
||||
{
|
||||
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
|
||||
NSString* asidUrl = [[NSString alloc] initWithFormat:@"%@update/filetable.bin?%@",urlpath,assetsidStr];
|
||||
NSLog(@"[dcc] %@", asidUrl);
|
||||
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:asidUrl] completionHandler:^(NSData * data, NSURLResponse * response, NSError * error)
|
||||
{
|
||||
NSMutableString* txtdcc = [[NSMutableString alloc]init];
|
||||
NSLog(@"[dcc] filetable.bin download complete");
|
||||
if (error)
|
||||
{
|
||||
NSLog(@"[dcc] filetable.bin download error");
|
||||
}
|
||||
if (data == nil || data.length % 8 != 0)
|
||||
{
|
||||
NSLog(@"[dcc] filetable.bin length error");
|
||||
}
|
||||
else
|
||||
{
|
||||
int32_t* v = (int32_t*)data.bytes;
|
||||
if (v[0] != 0xffeeddcc || v[1] != 1)
|
||||
{
|
||||
NSLog(@"dcc.bin file err!");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v[2] == 0x00ffffff)
|
||||
{
|
||||
int stp = (4 + 8) / 2;
|
||||
NSData* md5 = [data subdataWithRange:NSMakeRange(4*4, 8*4)];
|
||||
NSString* so = [[NSString alloc] initWithData:md5 encoding:NSUTF8StringEncoding];
|
||||
NSLog(@"--------------------------------------------");
|
||||
NSLog(@"so=%@",so);
|
||||
NSLog(@"netid=%@",assetsidStr);
|
||||
if ([so isEqualToString:assetsidStr])
|
||||
{
|
||||
for (NSUInteger ii = stp, isz = data.length / (2*4); ii < isz; ii++)
|
||||
{
|
||||
NSData* data1 = [data subdataWithRange:NSMakeRange(ii * 2 * 4, 4)];
|
||||
NSData* data2 = [data subdataWithRange:NSMakeRange(ii * 2 * 4 + 4, 4)];
|
||||
NSString* dccstr = [[NSString alloc] initWithFormat:@"%@ %@\n",[self convertDataToHexStr:data1],[self convertDataToHexStr:data2]];
|
||||
[txtdcc appendString:dccstr];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"error old format unsupport");
|
||||
}
|
||||
}
|
||||
}
|
||||
if ([txtdcc length] > 0)
|
||||
{
|
||||
laya::LayaCacheManager::getInstance()->doUpdateDcc(gRedistPath,urlpath.UTF8String,txtdcc.UTF8String,assetsidStr.UTF8String);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
[self requestGame:url];
|
||||
}];
|
||||
[task resume];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self requestGame:url];
|
||||
}
|
||||
}
|
||||
}];
|
||||
[task resume];
|
||||
}
|
||||
-(void)requestGame:(NSString*)url
|
||||
{
|
||||
NSURL* nsurl = [[NSURL alloc] initWithString:url];
|
||||
NSString* caturl = nil;
|
||||
if (nsurl.port)
|
||||
{
|
||||
caturl = [[NSString alloc] initWithFormat:@"%@://%@:%@",nsurl.scheme, nsurl.host, nsurl.port];
|
||||
}
|
||||
else
|
||||
{
|
||||
caturl = [[NSString alloc] initWithFormat:@"%@://%@",nsurl.scheme, nsurl.host];
|
||||
}
|
||||
NSLog(@"%@", caturl);
|
||||
// Create server
|
||||
_GCDWebServer = [[GCDWebServer alloc] init];
|
||||
|
||||
// Add a handler to respond to GET requests on any URL
|
||||
[_GCDWebServer addDefaultHandlerForMethod:@"GET"
|
||||
requestClass:[GCDWebServerRequest class]
|
||||
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||
NSString* urlOCStr = nil;
|
||||
if (![request.path hasPrefix:@"/s"]) {
|
||||
urlOCStr =[[NSString alloc] initWithFormat:@"%@%@",caturl, request.URL.resourceSpecifier];
|
||||
//TODO 没考虑 ?
|
||||
}
|
||||
else {
|
||||
urlOCStr = [request.URL.absoluteString stringByReplacingOccurrencesOfString:_urlSuffix withString:@""];
|
||||
}
|
||||
if ([urlOCStr containsString:@"http://stand.alone.version/"])
|
||||
{
|
||||
NSURL* tempUrl = [NSURL URLWithString:urlOCStr];
|
||||
if (tempUrl.query && ![tempUrl.query isEqualToString:@""] )
|
||||
{
|
||||
urlOCStr = [urlOCStr stringByReplacingOccurrencesOfString:[[NSString alloc] initWithFormat:@"?%@", tempUrl.query] withString:@""];
|
||||
}
|
||||
}
|
||||
|
||||
NSString* contentType = GCDWebServerGetMimeTypeForExtension([[request path] pathExtension], nil);
|
||||
const char* pURL = urlOCStr.UTF8String;
|
||||
laya::LayaCacheManager::getInstance()->handleRequest(pURL, [contentType,completionBlock,request](void* pData, int length) {
|
||||
NSData *data = [[NSData alloc] initWithBytes:pData length:length];
|
||||
|
||||
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:data contentType:contentType];
|
||||
completionBlock(response);
|
||||
|
||||
},[completionBlock](int errCode){
|
||||
//NSLog(@"dowlonad error [%d]",errCode);
|
||||
completionBlock(nil);
|
||||
});
|
||||
}];
|
||||
NSError *error = nil;
|
||||
int httpPort = (_hostPort != 0) ? _hostPort :12344;
|
||||
|
||||
NSMutableDictionary *options = [NSMutableDictionary dictionary];
|
||||
[options setObject:[NSNumber numberWithBool:YES] forKey:GCDWebServerOption_BindToLocalhost];
|
||||
[options setObject:@"GCD Web Server" forKey:GCDWebServerOption_ServerName];
|
||||
|
||||
do {
|
||||
NSLog(@"Starting http daemon port %d",httpPort);
|
||||
[options setObject:[NSNumber numberWithInteger:httpPort++] forKey:GCDWebServerOption_Port];
|
||||
} while(![_GCDWebServer startWithOptions:options error:&error]);
|
||||
|
||||
if (error) {
|
||||
NSLog(@"Error starting http daemon: %@", error);
|
||||
} else {
|
||||
//[_GCDWebServer setLogLevel:kGCDWebServerLoggingLevel_Warning];
|
||||
NSLog(@"Started http daemon port %lu",(unsigned long)_GCDWebServer.port);
|
||||
}
|
||||
[self loadUrl:[self getLayaBoxUrl]];
|
||||
_urlSuffix = [NSString stringWithFormat:@"http://127.0.0.1:%lu/s/", (unsigned long)(_GCDWebServer.port)];
|
||||
}
|
||||
- (void)loadUrl:(NSString*)url
|
||||
{
|
||||
NSURL* pUrl = [NSURL URLWithString:url ];
|
||||
NSURLRequest* pUrlRequest = [NSURLRequest requestWithURL:pUrl];
|
||||
[_webview loadRequest:pUrlRequest];
|
||||
}
|
||||
- (NSString*)callMethod:(int)objid className:(NSString*)cls methodName:(NSString*)method param:(NSString*)param
|
||||
{
|
||||
return [_reflection callMethod:objid className:cls methodName:method param:param];
|
||||
}
|
||||
-(void)callbackToJSWithClass:(Class)cls methodName:(NSString*)name ret:(NSObject*)retObj
|
||||
{
|
||||
[_reflection callbackToJSWithClass:cls methodName:name ret:retObj];
|
||||
}
|
||||
-(void)callbackToJSWithClassName:(NSString*)cls methodName:(NSString*)name ret:(NSObject*)retObj
|
||||
{
|
||||
[_reflection callbackToJSWithClassName:cls methodName:name ret:retObj];
|
||||
}
|
||||
-(void)callbackToJSWithObject:(id)obj methodName:(NSString*)name ret:(NSObject*)retObj
|
||||
{
|
||||
[_reflection callbackToJSWithObject:obj methodName:name ret:retObj];
|
||||
}
|
||||
-(void)runJS:(NSString*)script
|
||||
{
|
||||
[self.webview evaluateJavaScript:script completionHandler:^(id _Nullable response, NSError * _Nullable error) {
|
||||
if (response || error)
|
||||
{
|
||||
NSLog(@"value: %@ error: %@", response, error);
|
||||
|
||||
}
|
||||
}];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
@file conchConfig.h
|
||||
@brief 配置用到的,比如版本号 或者描述信息
|
||||
@author James
|
||||
@version 1.0
|
||||
@date 2013_7_5
|
||||
@company LayaBox
|
||||
*/
|
||||
#import "UIKit/UIKit.h"
|
||||
#import <GLKit/GLKit.h>
|
||||
#import <string>
|
||||
|
||||
@interface conchConfig : NSObject
|
||||
{
|
||||
@public
|
||||
|
||||
NSString* m_sAppVersion; //对外版本号
|
||||
NSString* m_sAppLocalVersion; //对内版本号
|
||||
NSString* m_sLaya8Url; //如果是Laya8启动,启动的url
|
||||
std::string m_sBackgroundcolor; //背景色
|
||||
NSString* m_sGameID; //appStroe用到的
|
||||
bool m_bCheckNetwork; //是否检查网络
|
||||
bool m_bNotification; //是否打开推送
|
||||
bool m_bShowAssistantTouch; //是否显示AssitantTouch
|
||||
/*
|
||||
UIInterfaceOrientationMaskPortrait, ===2
|
||||
UIInterfaceOrientationMaskPortraitUpsideDown, ===4
|
||||
UIInterfaceOrientationMaskLandscapeLeft, ===8
|
||||
UIInterfaceOrientationMaskLandscapeRight, ===16
|
||||
*/
|
||||
int m_nOrientationType; //游戏的方向
|
||||
NSString* m_sUrl; //游戏url
|
||||
int m_nHostPort; //hostPort
|
||||
}
|
||||
+(conchConfig*)GetInstance;
|
||||
-(bool)readIni;
|
||||
-(conchConfig*)init;
|
||||
@end
|
||||
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
@file conchConfig.h
|
||||
@brief 配置用到的,比如版本号 或者描述信息
|
||||
@author wyw
|
||||
@version 1.0
|
||||
@date 2013_7_5
|
||||
@company JoyChina
|
||||
*/
|
||||
|
||||
#import "conchConfig.h"
|
||||
#import <util/JCIniFile.h>
|
||||
#import <string>
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
static conchConfig* g_pConchConfig = nil;
|
||||
//-------------------------------------------------------------------------------
|
||||
@implementation conchConfig
|
||||
//-------------------------------------------------------------------------------
|
||||
+(conchConfig*)GetInstance
|
||||
{
|
||||
if( g_pConchConfig == nil )
|
||||
{
|
||||
g_pConchConfig = [[conchConfig alloc] init];
|
||||
}
|
||||
return g_pConchConfig;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
-(conchConfig*)init
|
||||
{
|
||||
self = [super init];
|
||||
m_sLaya8Url=nil; //如果是Laya8启动,启动的url
|
||||
m_sBackgroundcolor="#FFFFFF";//背景色
|
||||
m_sGameID=nil; //appStroe用到的
|
||||
m_bCheckNetwork=true; //是否检查网络
|
||||
m_bNotification=false; //是否打开消息推送
|
||||
m_nOrientationType = 30; //屏幕的方向
|
||||
m_sUrl=nil;
|
||||
m_nHostPort=0;
|
||||
m_bShowAssistantTouch = false;
|
||||
[self readIni];
|
||||
m_sAppVersion=nil; //版本号
|
||||
m_sAppLocalVersion = nil; //对内版本号
|
||||
NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
|
||||
// 当前应用软件版本 Bundle versions string, short
|
||||
m_sAppVersion = [infoDictionary objectForKey:@"CFBundleShortVersionString"];
|
||||
NSLog(@"当前应用软件版本:%@",m_sAppVersion);
|
||||
// 当前应用版本号码 Bundle versions
|
||||
m_sAppLocalVersion = [infoDictionary objectForKey:@"CFBundleVersion"];
|
||||
NSLog(@"当前应用Local版本号码:%@",m_sAppLocalVersion);
|
||||
return self;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
-(bool) readIni
|
||||
{
|
||||
std::string sIniFileName = [[self getResourcePath] cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
sIniFileName += "/config.ini";
|
||||
// 初始化 IAP
|
||||
laya::JCIniFile *pConfigFile = laya::JCIniFile::loadFile( sIniFileName.c_str() );
|
||||
|
||||
if( 0 == pConfigFile )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* sGameID=pConfigFile->GetValue("gameID");
|
||||
const char* sBackgroundColor=pConfigFile->GetValue("backgroundColor");
|
||||
const char* sCheckNetwork=pConfigFile->GetValue("checkNetwork");
|
||||
const char* sOrientation=pConfigFile->GetValue("orientation");
|
||||
const char* sHostPort = pConfigFile->GetValue("hostport");
|
||||
const char* sUrl = pConfigFile->GetValue("url");
|
||||
const char* sNotification = pConfigFile->GetValue("notification");
|
||||
const char* sAssistantTouch = pConfigFile->GetValue("assistantTouch");
|
||||
if( sGameID )
|
||||
{
|
||||
m_sGameID = [[NSString alloc] initWithUTF8String:sGameID ];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"读取ini gameID 错误");
|
||||
}
|
||||
if( sBackgroundColor )
|
||||
{
|
||||
m_sBackgroundcolor = sBackgroundColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"读取ini backgroundColor 错误");
|
||||
}
|
||||
if( sCheckNetwork )
|
||||
{
|
||||
m_bCheckNetwork = atoi(sCheckNetwork)>0;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"读取ini checkNetworkd 错误");
|
||||
}
|
||||
if( sOrientation )
|
||||
{
|
||||
m_nOrientationType = atoi(sOrientation);
|
||||
if( m_nOrientationType < 1 )
|
||||
{
|
||||
NSLog(@"读取ini orientation 错误");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"读取ini orientation错误");
|
||||
}
|
||||
if( sHostPort )
|
||||
{
|
||||
m_nHostPort = atoi(sHostPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"读取ini hostport错误");
|
||||
}
|
||||
if( sUrl )
|
||||
{
|
||||
m_sUrl = [[NSString alloc]initWithUTF8String:sUrl];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"读取ini url错误");
|
||||
}
|
||||
if( sNotification )
|
||||
{
|
||||
m_bNotification = atoi(sNotification)>0;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"读取ini notification 错误");
|
||||
}
|
||||
if( sAssistantTouch )
|
||||
{
|
||||
m_bShowAssistantTouch = atoi(sAssistantTouch)>0;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"读取ini assistantTouch 错误");
|
||||
}
|
||||
delete pConfigFile;
|
||||
pConfigFile = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
-(NSString*) getResourcePath
|
||||
{
|
||||
return [[NSBundle mainBundle] resourcePath];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
@file
|
||||
@brief
|
||||
@author
|
||||
@version 1.0
|
||||
@date
|
||||
@company LayaBox
|
||||
*/
|
||||
#import <objc/NSObject.h>
|
||||
#import <objc/objc.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSInvocation.h>
|
||||
#import "LayaWKWebview.h"
|
||||
|
||||
@interface Reflection : NSObject
|
||||
-(id)init:(LayaWKWebview*)webview;
|
||||
-(void)clearReflectionObjects;
|
||||
-(NSString*)callMethod:(int)objid className:(NSString*)cls methodName:(NSString*)method param:(NSString*)param;
|
||||
-(void)callbackToJSWithClass:(Class)cls methodName:(NSString*)name ret:(NSObject*)retObj;
|
||||
-(void)callbackToJSWithClassName:(NSString*)cls methodName:(NSString*)name ret:(NSObject*)retObj;
|
||||
-(void)callbackToJSWithObject:(id)obj methodName:(NSString*)name ret:(NSObject*)retObj;
|
||||
@end
|
||||
@@ -0,0 +1,221 @@
|
||||
#import "refection.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <string>
|
||||
//#import "../../../../source/conch/CToObjectC.h"
|
||||
//#import "CToObjectCIOS.h"
|
||||
//#import "../../../../../source/conch/JCScriptRuntime.h"
|
||||
//#import "JCThreadCmdMgr.h"
|
||||
|
||||
extern void reflectionCallback(const std::string& jsonret);
|
||||
|
||||
@implementation Reflection
|
||||
{
|
||||
NSMutableDictionary* m_pIDToObjectDic;
|
||||
NSMutableDictionary* m_pObjectToIDDic;
|
||||
NSLock* m_lock;
|
||||
LayaWKWebview* m_webview;
|
||||
}
|
||||
-(id)init:(LayaWKWebview*)webview;
|
||||
{
|
||||
self = [super init];
|
||||
if( self != nil )
|
||||
{
|
||||
m_webview = webview;
|
||||
m_pIDToObjectDic = [NSMutableDictionary dictionary];
|
||||
m_pObjectToIDDic = [NSMutableDictionary dictionary];
|
||||
m_lock = [NSLock new];
|
||||
return self;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
-(void)clearReflectionObjects
|
||||
{
|
||||
[m_lock lock];
|
||||
[m_pIDToObjectDic removeAllObjects];
|
||||
[m_pObjectToIDDic removeAllObjects];
|
||||
[m_lock unlock];
|
||||
}
|
||||
-(NSString*)invoke: (Class) objc_class isStatic:(BOOL) isStatic target:(id)target select:(SEL)select Param:(NSString*)param
|
||||
{
|
||||
NSMethodSignature* signature = isStatic ? [objc_class methodSignatureForSelector:select] : [objc_class instanceMethodSignatureForSelector:select];
|
||||
if (signature == nil){
|
||||
NSLog(@"reflection error: can not find method signature");
|
||||
return @"{}";
|
||||
}
|
||||
|
||||
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
if (invocation == nil){
|
||||
NSLog(@"reflection error: can not find invocation");
|
||||
return @"{}";
|
||||
}
|
||||
|
||||
[invocation setTarget:target];
|
||||
[invocation setSelector:select];
|
||||
|
||||
|
||||
NSUInteger numArgs = [signature numberOfArguments];
|
||||
|
||||
NSData* jsonData = [param dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (jsonData == nil){
|
||||
NSLog(@"reflection error");
|
||||
return @"{}";
|
||||
}
|
||||
|
||||
NSError* error = nil;
|
||||
id idPara = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
|
||||
|
||||
if (idPara == nil || ![idPara isKindOfClass:[NSArray class]]){
|
||||
NSLog(@"reflection error");
|
||||
return @"{}";
|
||||
}
|
||||
|
||||
NSArray* paraArray = idPara;
|
||||
NSUInteger num = [paraArray count];
|
||||
|
||||
if (numArgs != num + 2){
|
||||
NSLog(@"reflection error: argument number is [%lu] but need [%lu]",(unsigned long)num,(unsigned long)(numArgs-2));
|
||||
return @"{}";
|
||||
}
|
||||
|
||||
|
||||
for(int i = 2; i < num + 2; i++){
|
||||
NSObject* obj = [paraArray objectAtIndex:i-2];
|
||||
[invocation setArgument:&obj atIndex:i];
|
||||
}
|
||||
|
||||
[invocation invoke];
|
||||
|
||||
if (!strcmp(signature.methodReturnType, @encode(void))) {
|
||||
return @"{}";
|
||||
}
|
||||
|
||||
const char* methodReturnType = signature.methodReturnType;
|
||||
if (!strcmp(methodReturnType, "@")) {
|
||||
NSLog(@"method return type %s",methodReturnType);
|
||||
id returnValue;
|
||||
[invocation getReturnValue:&returnValue];
|
||||
NSDictionary* dic = [NSDictionary dictionaryWithObjectsAndKeys:returnValue, @"v",nil];
|
||||
NSError* pError = nil;
|
||||
NSData* pJsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&pError];
|
||||
if (pError) {
|
||||
NSLog(@"%@ %@", pError.debugDescription, pError.description);
|
||||
return @"{}";
|
||||
}
|
||||
return [[NSString alloc] initWithData:pJsonData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
else {
|
||||
NSLog(@"method return type %s not supported",methodReturnType);
|
||||
return @"{}";
|
||||
}
|
||||
return @"{}";
|
||||
}
|
||||
|
||||
-(NSString*)callMethod:(int)objid className:(NSString*)cls methodName:(NSString*)method param:(NSString*)param
|
||||
{
|
||||
Class objc_class = NSClassFromString(cls);
|
||||
if (objc_class == nil){
|
||||
NSLog(@"reflection error : can not find class [%@]",cls);
|
||||
return @"{}";
|
||||
}
|
||||
|
||||
if (objid == -1){
|
||||
|
||||
SEL select = NSSelectorFromString(method);
|
||||
if (select == 0){
|
||||
NSLog(@"reflection error");
|
||||
return @"{}";
|
||||
}
|
||||
return [self invoke:objc_class isStatic:TRUE target:objc_class select:select Param:param];
|
||||
}
|
||||
else{
|
||||
|
||||
if ([method isEqualToString:@"<init>"]){//构造函数
|
||||
NSObject* object = [[objc_class alloc] init];
|
||||
if (object == nil){
|
||||
NSLog(@"reflection error : alloc init class [%@] object failed",cls);
|
||||
return @"{}";
|
||||
}
|
||||
[m_lock lock];
|
||||
[m_pIDToObjectDic setObject:object forKey:[NSNumber numberWithInt:objid]];
|
||||
[m_pObjectToIDDic setObject:[NSNumber numberWithInt:objid] forKey:[NSNumber numberWithInteger:(NSInteger)object]];
|
||||
[m_lock unlock];
|
||||
}
|
||||
else{
|
||||
NSObject* object = [m_pIDToObjectDic objectForKey:[NSNumber numberWithInt:objid]];
|
||||
|
||||
if (object == nil){
|
||||
NSLog(@"reflection error : can not find object id [%i]",objid);
|
||||
return @"{}";
|
||||
}
|
||||
|
||||
if (![object isKindOfClass:objc_class]){
|
||||
NSLog(@"reflection error : object of id [%i] is not king of class [%@]",objid,cls);
|
||||
return @"{}";
|
||||
}
|
||||
|
||||
SEL select = NSSelectorFromString(method);
|
||||
if (select == 0){
|
||||
NSLog(@"reflection error");
|
||||
return @"{}";
|
||||
}
|
||||
return [self invoke:objc_class isStatic:FALSE target:object select:select Param:param];
|
||||
}
|
||||
}
|
||||
return @"{}";
|
||||
}
|
||||
|
||||
-(void)callbackToJSWithClass:(Class)cls methodName:(NSString*)name ret:(NSObject*)retObj
|
||||
{
|
||||
[self callbackToJSWithClassName:NSStringFromClass(cls) methodName:name ret:retObj];
|
||||
}
|
||||
|
||||
-(void)callbackToJSWithClassName:(NSString*)cls methodName:(NSString*)name ret:(NSObject*)retObj
|
||||
{
|
||||
NSMutableDictionary* dic = [NSMutableDictionary dictionary];
|
||||
|
||||
[dic setObject:@-1 forKey:@"objId"];
|
||||
[dic setObject:cls forKey:@"cName"];
|
||||
[dic setObject:name forKey:@"mName"];
|
||||
if (retObj == nil)
|
||||
[dic setObject:@"" forKey:@"v"];
|
||||
else
|
||||
[dic setObject:retObj forKey:@"v"];
|
||||
|
||||
NSError* error = nil;
|
||||
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dic options:0 error:&error];
|
||||
if (error != nil){
|
||||
NSLog(@"callbackToJS error");
|
||||
return;
|
||||
}
|
||||
NSString* jason = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
NSString* js = [NSString stringWithFormat:@"window.conchPlatCallBack(%@)", jason];
|
||||
js = [[js stringByReplacingOccurrencesOfString:@"\n" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||
[m_webview runJS:js];
|
||||
NSLog(@"run js: %@", js);
|
||||
}
|
||||
-(void)callbackToJSWithObject:(id)obj methodName:(NSString*)name ret:(NSObject*)retObj
|
||||
{
|
||||
NSMutableDictionary* dic = [NSMutableDictionary dictionary];
|
||||
[m_lock lock];
|
||||
[dic setObject:[m_pObjectToIDDic objectForKey:[NSNumber numberWithInteger:(NSInteger)obj]] forKey:@"objId"];
|
||||
[m_lock unlock];
|
||||
[dic setObject:@"" forKey:@"cName"];
|
||||
[dic setObject:name forKey:@"mName"];
|
||||
if (retObj == nil)
|
||||
[dic setObject:@"" forKey:@"v"];
|
||||
else
|
||||
[dic setObject:retObj forKey:@"v"];
|
||||
|
||||
NSError* error = nil;
|
||||
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dic options:0 error:&error];
|
||||
if (error != nil){
|
||||
NSLog(@"callbackToJS error");
|
||||
return;
|
||||
}
|
||||
NSString* jason = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
NSString* js = [NSString stringWithFormat:@"window.conchPlatCallBack(%@)", jason];
|
||||
js = [[js stringByReplacingOccurrencesOfString:@"\n" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||
[m_webview runJS:js];
|
||||
NSLog(@"run js: %@", js);
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/bin/sh
|
||||
|
||||
rm -rf WKWebview/libs/libLayaWKWebview.a
|
||||
|
||||
#—————————————————————merge static lib————————————————————————
|
||||
rm -rf armv7a
|
||||
rm -rf armv64
|
||||
rm -rf i386
|
||||
rm -rf x86_64
|
||||
mkdir armv7a
|
||||
mkdir armv64
|
||||
mkdir i386
|
||||
mkdir x86_64
|
||||
|
||||
|
||||
lipo -extract armv7 ../../ThirdParty/curl/lib/ios/libcurl.a -o armv7a/libcurl.a
|
||||
lipo -extract arm64 ../../ThirdParty/curl/lib/ios/libcurl.a -o armv64/libcurl.a
|
||||
lipo -extract i386 ../../ThirdParty/curl/lib/ios/libcurl.a -o i386/libcurl.a
|
||||
lipo -extract x86_64 ../../ThirdParty/curl/lib/ios/libcurl.a -o x86_64/libcurl.a
|
||||
|
||||
lipo -extract armv7 ../../ThirdParty/openssl/lib/ios/libssl.a -o armv7a/libssl.a
|
||||
lipo -extract arm64 ../../ThirdParty/openssl/lib/ios/libssl.a -o armv64/libssl.a
|
||||
lipo -extract i386 ../../ThirdParty/openssl/lib/ios/libssl.a -o i386/libssl.a
|
||||
lipo -extract x86_64 ../../ThirdParty/openssl/lib/ios/libssl.a -o x86_64/libssl.a
|
||||
|
||||
lipo -extract armv7 ../../ThirdParty/openssl/lib/ios/libcrypto.a -o armv7a/libcrypto.a
|
||||
lipo -extract arm64 ../../ThirdParty/openssl/lib/ios/libcrypto.a -o armv64/libcrypto.a
|
||||
lipo -extract i386 ../../ThirdParty/openssl/lib/ios/libcrypto.a -o i386/libcrypto.a
|
||||
lipo -extract x86_64 ../../ThirdParty/openssl/lib/ios/libcrypto.a -o x86_64/libcrypto.a
|
||||
|
||||
|
||||
#lipo -extract armv7 ../../ThirdParty/tpg/lib/ios/libTPGDec.a -o armv7a/libTPGDec.a
|
||||
#lipo -extract arm64 ../../ThirdParty/tpg/lib/ios/libTPGDec.a -o armv64/libTPGDec.a
|
||||
#lipo -extract i386 ../../ThirdParty/tpg/lib/ios/libTPGDec.a -o i386/libTPGDec.a
|
||||
#lipo -extract x86_64 ../../ThirdParty/tpg/lib/ios/libTPGDec.a -o x86_64/libTPGDec.a
|
||||
|
||||
#lipo -extract armv7 ../../ThirdParty/tpg/lib/ios/liblibYUV.a -o armv7a/liblibYUV.a
|
||||
#lipo -extract arm64 ../../ThirdParty/tpg/lib/ios/liblibYUV.a -o armv64/liblibYUV.a
|
||||
#lipo -extract i386 ../../ThirdParty/tpg/lib/ios/liblibYUV.a -o i386/liblibYUV.a
|
||||
#lipo -extract x86_64 ../../ThirdParty/tpg/lib/ios/liblibYUV.a -o x86_64/liblibYUV.a
|
||||
|
||||
lipo -extract armv7 ../../Conch/libs/ios/libcommon.a -o armv7a/libcommon.a
|
||||
lipo -extract arm64 ../../Conch/libs/ios/libcommon.a -o armv64/libcommon.a
|
||||
lipo -extract i386 ../../Conch/libs/ios-sim/libcommon.a -o i386/libcommon.a
|
||||
lipo -extract x86_64 ../../Conch/libs/ios-sim/libcommon.a -o x86_64/libcommon.a
|
||||
|
||||
lipo -extract armv7 ../../Conch/libs/ios/libLayaWKWebview.a -o armv7a/libLayaWKWebview.a
|
||||
lipo -extract arm64 ../../Conch/libs/ios/libLayaWKWebview.a -o armv64/libLayaWKWebview.a
|
||||
lipo -extract i386 ../../Conch/libs/ios-sim/libLayaWKWebview.a -o i386/libLayaWKWebview.a
|
||||
lipo -extract x86_64 ../../Conch/libs/ios-sim/libLayaWKWebview.a -o x86_64/libLayaWKWebview.a
|
||||
|
||||
cd armv7a
|
||||
|
||||
libtool -static *.a -o libLayaWK.a
|
||||
|
||||
cd ..
|
||||
cd armv64
|
||||
|
||||
libtool -static *.a -o libLayaWK.a
|
||||
|
||||
cd ..
|
||||
cd i386
|
||||
|
||||
libtool -static *.a -o libLayaWK.a
|
||||
|
||||
cd ..
|
||||
cd x86_64
|
||||
|
||||
libtool -static *.a -o libLayaWK.a
|
||||
|
||||
cd ..
|
||||
|
||||
lipo -create x86_64/libLayaWK.a i386/libLayaWK.a armv7a/libLayaWK.a armv64/libLayaWK.a -output ../../publish/nativetools/template/wkwebview/libs/libLayaWK.a
|
||||
|
||||
rm -rf armv7a
|
||||
rm -rf armv64
|
||||
rm -rf i386
|
||||
rm -rf x86_64
|
||||
|
||||
#—————————————————————copy .h————————————————————————
|
||||
cp -f LayaWKWebview/proj.ios/LayaWKWebview/conchConfig.h ../../publish/nativetools/template/wkwebview/libs/include/conchConfig.h
|
||||
cp -f LayaWKWebview/proj.ios/LayaWKWebview/LayaWKWebview.h ../../publish/nativetools/template/wkwebview/libs/include/LayaWKWebview.h
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
cd common/proj.android_studio/jni/
|
||||
ndk-build
|
||||
cd ../../../
|
||||
|
||||
cd webglPlus/proj.android_studio/jni/
|
||||
ndk-build
|
||||
cd ../../../
|
||||
|
||||
cd render/proj.android_studio/jni/
|
||||
ndk-build
|
||||
cd ../../../
|
||||
|
||||
cp common/proj.android_studio/obj/local/armeabi-v7a/libcommon.a ../libs/android-armv7/
|
||||
cp render/proj.android_studio/obj/local/armeabi-v7a/librender.a ../libs/android-armv7/
|
||||
cp webglPlus/proj.android_studio/obj/local/armeabi-v7a/libwebglPlus.a ../libs/android-armv7/
|
||||
|
||||
cp common/proj.android_studio/obj/local/arm64-v8a/libcommon.a ../libs/android-arm64/
|
||||
cp render/proj.android_studio/obj/local/arm64-v8a/librender.a ../libs/android-arm64/
|
||||
cp webglPlus/proj.android_studio/obj/local/arm64-v8a/libwebglPlus.a ../libs/android-arm64/
|
||||
|
||||
cp common/proj.android_studio/obj/local/x86/libcommon.a ../libs/android-x86/
|
||||
cp render/proj.android_studio/obj/local/x86/librender.a ../libs/android-x86/
|
||||
cp webglPlus/proj.android_studio/obj/local/x86/libwebglPlus.a ../libs/android-x86/
|
||||
|
||||
#cp common/proj.android_studio/obj/local/armeabi/libcommon.a ../libs/android-arm/
|
||||
#cp render/proj.android_studio/obj/local/armeabi/librender.a ../libs/android-arm/
|
||||
|
||||
touch ../source/conch/JCConch.cpp
|
||||
cd conch/proj.android_studio/jni/
|
||||
ndk-build
|
||||
cd ../../../
|
||||
cp conch/proj.android_studio/libs/arm64-v8a/liblayaair.so conch/proj.android_studio/conch5/libs/arm64-v8a/liblayaair.so
|
||||
cp conch/proj.android_studio/libs/armeabi-v7a/liblayaair.so conch/proj.android_studio/conch5/libs/armeabi-v7a/liblayaair.so
|
||||
cp conch/proj.android_studio/libs/x86/liblayaair.so conch/proj.android_studio/conch5/libs/x86/liblayaair.so
|
||||
|
||||
cp conch/proj.android_studio/libs/ conch/proj.android_studio/conch5/ -f -R
|
||||
@@ -0,0 +1 @@
|
||||
obj
|
||||
@@ -0,0 +1,113 @@
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
#libcommon.a
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libcommon
|
||||
LOCAL_CFLAGS := \
|
||||
-fexceptions \
|
||||
-Wno-multichar \
|
||||
-DANDROID \
|
||||
-DIN_LIBRARY \
|
||||
-D_GLIBCXX_PERMIT_BACKWARD_HASH \
|
||||
-frtti
|
||||
|
||||
LOCAL_CFLAGS += -std=c++11
|
||||
|
||||
ifeq ($(APP_PERFTEST),1)
|
||||
LOCAL_CFLAGS += -DPERFTEST
|
||||
endif
|
||||
|
||||
LOCAL_CXXFLAGS := -O3
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
../../../../source/common/buffer/JCBuffer.cpp \
|
||||
../../../../source/common/downloadCache/JCFileTable.cpp \
|
||||
../../../../source/common/downloadCache/JCServerFileCache.cpp \
|
||||
../../../../source/common/downloadMgr/JCCurlWrap.cpp \
|
||||
../../../../source/common/downloadMgr/JCHttpHeader.cpp \
|
||||
../../../../source/common/downloadMgr/JCDownloadMgr.cpp \
|
||||
../../../../source/common/event/JCEmitter.cpp \
|
||||
../../../../source/common/event/JCEventBase.cpp \
|
||||
../../../../source/common/fileSystem/JCFileSystem.cpp \
|
||||
../../../../source/common/fontMgr/JCFreeTypeRender.cpp \
|
||||
../../../../source/common/imageLib/JCImageRW.cpp \
|
||||
../../../../source/common/imageLib/JCJpegImg.cpp \
|
||||
../../../../source/common/imageLib/JCPngImg.cpp \
|
||||
../../../../source/common/imageLib/JCGifImg.cpp \
|
||||
../../../../source/common/math/Matrix32.cpp \
|
||||
../../../../source/common/misc/JCGetClockExact.cpp \
|
||||
../../../../source/common/misc/JCLayaThreadPool.cpp \
|
||||
../../../../source/common/misc/JCWorkerThread.cpp \
|
||||
../../../../source/common/misc/JCWorkSemaphore.cpp \
|
||||
../../../../source/common/util/JCColor.cpp \
|
||||
../../../../source/common/util/JCCommonMethed.cpp \
|
||||
../../../../source/common/util/JCCrypto.cpp \
|
||||
../../../../source/common/util/JCJson.cpp \
|
||||
../../../../source/common/util/JCIniFile.cpp \
|
||||
../../../../source/common/util/JCLayaUrl.cpp \
|
||||
../../../../source/common/util/JCMemorySurvey.cpp \
|
||||
../../../../source/common/util/JCXml.cpp \
|
||||
../../../../source/common/util/JCZipFile.cpp \
|
||||
../../../../source/common/util/JCZlib.cpp \
|
||||
../../../../source/common/util/Log.cpp \
|
||||
../../../../source/common/util/JCFlog.cpp \
|
||||
../../../../source/common/resource/Audio/JCAudioWavplayer.cpp \
|
||||
../../../../source/common/resource/Audio/JCOggParser.cpp \
|
||||
../../../../source/common/resource/Audio/JCWaveInfo.cpp \
|
||||
../../../../source/common/resource/Audio/JCWaveParser.cpp \
|
||||
../../../../source/common/fontMgr/JCFontInfo.cpp \
|
||||
../../../../source/common/fontMgr/JCFontManager.cpp \
|
||||
../../../../source/common/resource/JCFileResManager.cpp \
|
||||
../../../../source/common/resource/JCResManager.cpp \
|
||||
../../../../source/common/resource/JCResource.cpp \
|
||||
../../../../source/common/OpenAL/OpenAL32/alAuxEffectSlot.c \
|
||||
../../../../source/common/OpenAL/OpenAL32/alBuffer.c \
|
||||
../../../../source/common/OpenAL/OpenAL32/alDatabuffer.c \
|
||||
../../../../source/common/OpenAL/OpenAL32/alEffect.c \
|
||||
../../../../source/common/OpenAL/OpenAL32/alError.c \
|
||||
../../../../source/common/OpenAL/OpenAL32/alExtension.c \
|
||||
../../../../source/common/OpenAL/OpenAL32/alFilter.c \
|
||||
../../../../source/common/OpenAL/OpenAL32/alListener.c \
|
||||
../../../../source/common/OpenAL/OpenAL32/alSource.c \
|
||||
../../../../source/common/OpenAL/OpenAL32/alState.c \
|
||||
../../../../source/common/OpenAL/OpenAL32/alThunk.c \
|
||||
../../../../source/common/OpenAL/Alc/ALc.c \
|
||||
../../../../source/common/OpenAL/Alc/alcConfig.c \
|
||||
../../../../source/common/OpenAL/Alc/alcEcho.c \
|
||||
../../../../source/common/OpenAL/Alc/alcModulator.c \
|
||||
../../../../source/common/OpenAL/Alc/alcReverb.c \
|
||||
../../../../source/common/OpenAL/Alc/alcRing.c \
|
||||
../../../../source/common/OpenAL/Alc/alcThread.c \
|
||||
../../../../source/common/OpenAL/Alc/ALu.c \
|
||||
../../../../source/common/OpenAL/Alc/android.c \
|
||||
../../../../source/common/OpenAL/Alc/bs2b.c \
|
||||
../../../../source/common/OpenAL/Alc/null.c \
|
||||
|
||||
|
||||
LOCAL_C_INCLUDES := ../../../../../ThirdParty/curl/include/android \
|
||||
../../../../../ThirdParty/png/include/android \
|
||||
../../../../../ThirdParty/jpeg/include/android \
|
||||
../../../../../ThirdParty/freetype/include/android \
|
||||
../../../../../ThirdParty/zip/include/android \
|
||||
../../../../../ThirdParty/ogg/include/android \
|
||||
../../../../../ThirdParty/zlib/include/android \
|
||||
../../../../source/common/OpenAL/include \
|
||||
../../../../source/common/OpenAL/OpenAL32/Include \
|
||||
../../../../source/common/glm \
|
||||
|
||||
#LOCAL_IS64:=32
|
||||
#LOCAL_LDLIBS := -llog -lz -landroid \
|
||||
# ../../../../libs/android$(LOCAL_IS64)/libcurl.a \
|
||||
# ../../../../libs/android$(LOCAL_IS64)/libgnustl_static.a \
|
||||
# ../../../../libs/android$(LOCAL_IS64)/libjpeg.a \
|
||||
# ../../../../libs/android$(LOCAL_IS64)/libpng.a \
|
||||
# ../../../../libs/android$(LOCAL_IS64)/libfreetype.a \
|
||||
# ../../../../libs/android$(LOCAL_IS64)/libzip.a \
|
||||
# ../../../../libs/android$(LOCAL_IS64)/libogg.a \
|
||||
# ../../../../libs/android$(LOCAL_IS64)/libvorbis.a \
|
||||
# ../../../../libs/android$(LOCAL_IS64)/libvorbis-jni.a \
|
||||
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
APP_MODULES := libcommon
|
||||
APP_PLATFORM := android-18
|
||||
NDK_TOOLCHAIN_VERSION := 4.9
|
||||
APP_STL := gnustl_static
|
||||
APP_OPTIM := release
|
||||
#使用硬件fp
|
||||
APP_ABI :=armeabi-v7a arm64-v8a x86
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
ndk-build NDK_PROJECT_PATH=. NDK_APP_APPLICATION_MK=./Application.mk APP_BUILD_SCRIPT=./Android.mk APP_ABI=armeabi-v7a obj/local/armeabi-v7a/libcommon.a LOCAL_ARM_MODE=arm
|
||||
#LOCAL_ARM_NEON=true ARCH_ARM_HAVE_NEON=true
|
||||
@@ -0,0 +1,14 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-17
|
||||
@@ -0,0 +1,805 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
24760D7F2081C3E600594375 /* JCFreeTypeRender.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 24760D772081C3E500594375 /* JCFreeTypeRender.cpp */; };
|
||||
24760D802081C3E600594375 /* JCFontInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 24760D792081C3E500594375 /* JCFontInfo.cpp */; };
|
||||
24760D812081C3E600594375 /* JCFontManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 24760D7C2081C3E500594375 /* JCFontManager.cpp */; };
|
||||
5E6806501D7D504C002A7575 /* JCHttpHeader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E68064E1D7D504C002A7575 /* JCHttpHeader.cpp */; };
|
||||
8A4296901B6757DB0072C3F7 /* libcommon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A4296841B6757DB0072C3F7 /* libcommon.a */; };
|
||||
9DFB1EDE1D34EB33006D31AD /* JCOggParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9DFB1EDC1D34EB33006D31AD /* JCOggParser.cpp */; };
|
||||
9DFB1F591D3632AB006D31AD /* JCGifImg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9DFB1F571D3632AB006D31AD /* JCGifImg.cpp */; };
|
||||
A242E3EB1D813D420013CCF0 /* JCFlog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A242E3E91D813D420013CCF0 /* JCFlog.cpp */; };
|
||||
A27D925B2106D9BD00CD920F /* Matrix32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A27D92532106D9BC00CD920F /* Matrix32.cpp */; };
|
||||
A2D117791D0B9B8B004C229D /* JCBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117771D0B9B8B004C229D /* JCBuffer.cpp */; };
|
||||
A2D117801D0B9B9B004C229D /* JCFileTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D1177B1D0B9B9B004C229D /* JCFileTable.cpp */; };
|
||||
A2D117811D0B9B9B004C229D /* JCServerFileCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D1177E1D0B9B9B004C229D /* JCServerFileCache.cpp */; };
|
||||
A2D117861D0B9BB0004C229D /* JCCurlWrap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117821D0B9BB0004C229D /* JCCurlWrap.cpp */; };
|
||||
A2D117871D0B9BB0004C229D /* JCDownloadMgr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117841D0B9BB0004C229D /* JCDownloadMgr.cpp */; };
|
||||
A2D1178C1D0B9BBE004C229D /* JCEmitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117881D0B9BBE004C229D /* JCEmitter.cpp */; };
|
||||
A2D1178D1D0B9BBE004C229D /* JCEventBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D1178A1D0B9BBE004C229D /* JCEventBase.cpp */; };
|
||||
A2D117901D0B9BCF004C229D /* JCFileSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D1178E1D0B9BCF004C229D /* JCFileSystem.cpp */; };
|
||||
A2D117A61D0B9BF5004C229D /* JCImageRW.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117A21D0B9BF5004C229D /* JCImageRW.cpp */; };
|
||||
A2D117A71D0B9BF5004C229D /* JCJpegImg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117A41D0B9BF5004C229D /* JCJpegImg.cpp */; };
|
||||
A2D117A81D0B9BF5004C229D /* JCPngImg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117A51D0B9BF5004C229D /* JCPngImg.cpp */; };
|
||||
A2D117BD1D0B9C20004C229D /* JCGetClockExact.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117B31D0B9C20004C229D /* JCGetClockExact.cpp */; };
|
||||
A2D117BE1D0B9C20004C229D /* JCLayaThreadPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117B51D0B9C20004C229D /* JCLayaThreadPool.cpp */; };
|
||||
A2D117BF1D0B9C20004C229D /* JCWorkerThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117B91D0B9C20004C229D /* JCWorkerThread.cpp */; };
|
||||
A2D117C01D0B9C20004C229D /* JCWorkSemaphore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117BB1D0B9C20004C229D /* JCWorkSemaphore.cpp */; };
|
||||
A2D117DC1D0B9C51004C229D /* JCAudioWavplayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117D31D0B9C51004C229D /* JCAudioWavplayer.cpp */; };
|
||||
A2D117DE1D0B9C51004C229D /* JCWaveInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117D81D0B9C51004C229D /* JCWaveInfo.cpp */; };
|
||||
A2D117DF1D0B9C51004C229D /* JCWaveParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117DA1D0B9C51004C229D /* JCWaveParser.cpp */; };
|
||||
A2D117F51D0B9D2C004C229D /* JCFileResManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117EF1D0B9D2C004C229D /* JCFileResManager.cpp */; };
|
||||
A2D117F61D0B9D2C004C229D /* JCResManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117F11D0B9D2C004C229D /* JCResManager.cpp */; };
|
||||
A2D117F71D0B9D2C004C229D /* JCResource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117F31D0B9D2C004C229D /* JCResource.cpp */; };
|
||||
A2D118111D0B9D51004C229D /* JCColor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117F81D0B9D51004C229D /* JCColor.cpp */; };
|
||||
A2D118121D0B9D51004C229D /* JCCommonMethed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117FA1D0B9D51004C229D /* JCCommonMethed.cpp */; };
|
||||
A2D118131D0B9D51004C229D /* JCCrypto.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117FC1D0B9D51004C229D /* JCCrypto.cpp */; };
|
||||
A2D118141D0B9D51004C229D /* JCJson.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D117FF1D0B9D51004C229D /* JCJson.cpp */; };
|
||||
A2D118151D0B9D51004C229D /* JCLayaUrl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D118011D0B9D51004C229D /* JCLayaUrl.cpp */; };
|
||||
A2D118161D0B9D51004C229D /* JCMemorySurvey.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D118031D0B9D51004C229D /* JCMemorySurvey.cpp */; };
|
||||
A2D118181D0B9D51004C229D /* JCXml.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D118081D0B9D51004C229D /* JCXml.cpp */; };
|
||||
A2D118191D0B9D51004C229D /* JCZipFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D1180A1D0B9D51004C229D /* JCZipFile.cpp */; };
|
||||
A2D1181A1D0B9D51004C229D /* JCZlib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D1180C1D0B9D51004C229D /* JCZlib.cpp */; };
|
||||
A2D1181B1D0B9D51004C229D /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D1180F1D0B9D51004C229D /* Log.cpp */; };
|
||||
A2D11A021D0C2D65004C229D /* JCIniFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A2D11A001D0C2D65004C229D /* JCIniFile.cpp */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
8A4296911B6757DB0072C3F7 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 8A42967C1B6757DB0072C3F7 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 8A4296831B6757DB0072C3F7;
|
||||
remoteInfo = common;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
8A4296821B6757DB0072C3F7 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "include/$(PRODUCT_NAME)";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
24760D772081C3E500594375 /* JCFreeTypeRender.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCFreeTypeRender.cpp; sourceTree = "<group>"; };
|
||||
24760D782081C3E500594375 /* JCFontManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCFontManager.h; sourceTree = "<group>"; };
|
||||
24760D792081C3E500594375 /* JCFontInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCFontInfo.cpp; sourceTree = "<group>"; };
|
||||
24760D7A2081C3E500594375 /* JCFontInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCFontInfo.h; sourceTree = "<group>"; };
|
||||
24760D7C2081C3E500594375 /* JCFontManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCFontManager.cpp; sourceTree = "<group>"; };
|
||||
24760D7D2081C3E500594375 /* JCFreeTypeRender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCFreeTypeRender.h; sourceTree = "<group>"; };
|
||||
5E68064E1D7D504C002A7575 /* JCHttpHeader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCHttpHeader.cpp; sourceTree = "<group>"; };
|
||||
5E68064F1D7D504C002A7575 /* JCHttpHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCHttpHeader.h; sourceTree = "<group>"; };
|
||||
8A4296841B6757DB0072C3F7 /* libcommon.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libcommon.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8A42968F1B6757DB0072C3F7 /* commonTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = commonTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8A4296951B6757DB0072C3F7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
8A42A0871B675CEF0072C3F7 /* rapidxml.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rapidxml.hpp; sourceTree = "<group>"; };
|
||||
8A42A0881B675CEF0072C3F7 /* rapidxml_iterators.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rapidxml_iterators.hpp; sourceTree = "<group>"; };
|
||||
8A42A0891B675CEF0072C3F7 /* rapidxml_print.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rapidxml_print.hpp; sourceTree = "<group>"; };
|
||||
8A42A08A1B675CEF0072C3F7 /* rapidxml_utils.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rapidxml_utils.hpp; sourceTree = "<group>"; };
|
||||
9DFB1EDC1D34EB33006D31AD /* JCOggParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCOggParser.cpp; sourceTree = "<group>"; };
|
||||
9DFB1EDD1D34EB33006D31AD /* JCOggParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCOggParser.h; sourceTree = "<group>"; };
|
||||
9DFB1F571D3632AB006D31AD /* JCGifImg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCGifImg.cpp; sourceTree = "<group>"; };
|
||||
9DFB1F581D3632AB006D31AD /* JCGifImg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCGifImg.h; sourceTree = "<group>"; };
|
||||
A242E3E91D813D420013CCF0 /* JCFlog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCFlog.cpp; sourceTree = "<group>"; };
|
||||
A242E3EA1D813D420013CCF0 /* JCFlog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCFlog.h; sourceTree = "<group>"; };
|
||||
A27D92532106D9BC00CD920F /* Matrix32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Matrix32.cpp; path = ../../../source/common/math/Matrix32.cpp; sourceTree = "<group>"; };
|
||||
A27D92572106D9BD00CD920F /* Matrix32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Matrix32.h; path = ../../../source/common/math/Matrix32.h; sourceTree = "<group>"; };
|
||||
A2D117771D0B9B8B004C229D /* JCBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCBuffer.cpp; sourceTree = "<group>"; };
|
||||
A2D117781D0B9B8B004C229D /* JCBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCBuffer.h; sourceTree = "<group>"; };
|
||||
A2D1177A1D0B9B9B004C229D /* JCFileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCFileSource.h; sourceTree = "<group>"; };
|
||||
A2D1177B1D0B9B9B004C229D /* JCFileTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCFileTable.cpp; sourceTree = "<group>"; };
|
||||
A2D1177C1D0B9B9B004C229D /* JCFileTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCFileTable.h; sourceTree = "<group>"; };
|
||||
A2D1177D1D0B9B9B004C229D /* JCIosFileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCIosFileSource.h; sourceTree = "<group>"; };
|
||||
A2D1177E1D0B9B9B004C229D /* JCServerFileCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCServerFileCache.cpp; sourceTree = "<group>"; };
|
||||
A2D1177F1D0B9B9B004C229D /* JCServerFileCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCServerFileCache.h; sourceTree = "<group>"; };
|
||||
A2D117821D0B9BB0004C229D /* JCCurlWrap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCCurlWrap.cpp; sourceTree = "<group>"; };
|
||||
A2D117831D0B9BB0004C229D /* JCCurlWrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCCurlWrap.h; sourceTree = "<group>"; };
|
||||
A2D117841D0B9BB0004C229D /* JCDownloadMgr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCDownloadMgr.cpp; sourceTree = "<group>"; };
|
||||
A2D117851D0B9BB0004C229D /* JCDownloadMgr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCDownloadMgr.h; sourceTree = "<group>"; };
|
||||
A2D117881D0B9BBE004C229D /* JCEmitter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCEmitter.cpp; sourceTree = "<group>"; };
|
||||
A2D117891D0B9BBE004C229D /* JCEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCEmitter.h; sourceTree = "<group>"; };
|
||||
A2D1178A1D0B9BBE004C229D /* JCEventBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCEventBase.cpp; sourceTree = "<group>"; };
|
||||
A2D1178B1D0B9BBE004C229D /* JCEventBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCEventBase.h; sourceTree = "<group>"; };
|
||||
A2D1178E1D0B9BCF004C229D /* JCFileSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCFileSystem.cpp; sourceTree = "<group>"; };
|
||||
A2D1178F1D0B9BCF004C229D /* JCFileSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCFileSystem.h; sourceTree = "<group>"; };
|
||||
A2D117A21D0B9BF5004C229D /* JCImageRW.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCImageRW.cpp; sourceTree = "<group>"; };
|
||||
A2D117A31D0B9BF5004C229D /* JCImageRW.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCImageRW.h; sourceTree = "<group>"; };
|
||||
A2D117A41D0B9BF5004C229D /* JCJpegImg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCJpegImg.cpp; sourceTree = "<group>"; };
|
||||
A2D117A51D0B9BF5004C229D /* JCPngImg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCPngImg.cpp; sourceTree = "<group>"; };
|
||||
A2D117B21D0B9C20004C229D /* JCCondition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCCondition.h; sourceTree = "<group>"; };
|
||||
A2D117B31D0B9C20004C229D /* JCGetClockExact.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCGetClockExact.cpp; sourceTree = "<group>"; };
|
||||
A2D117B41D0B9C20004C229D /* JCGetClockExact.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCGetClockExact.h; sourceTree = "<group>"; };
|
||||
A2D117B51D0B9C20004C229D /* JCLayaThreadPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCLayaThreadPool.cpp; sourceTree = "<group>"; };
|
||||
A2D117B61D0B9C20004C229D /* JCLayaThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCLayaThreadPool.h; sourceTree = "<group>"; };
|
||||
A2D117B71D0B9C20004C229D /* JCSingleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCSingleton.h; sourceTree = "<group>"; };
|
||||
A2D117B81D0B9C20004C229D /* JCThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCThreadPool.h; sourceTree = "<group>"; };
|
||||
A2D117B91D0B9C20004C229D /* JCWorkerThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCWorkerThread.cpp; sourceTree = "<group>"; };
|
||||
A2D117BA1D0B9C20004C229D /* JCWorkerThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCWorkerThread.h; sourceTree = "<group>"; };
|
||||
A2D117BB1D0B9C20004C229D /* JCWorkSemaphore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCWorkSemaphore.cpp; sourceTree = "<group>"; };
|
||||
A2D117BC1D0B9C20004C229D /* JCWorkSemaphore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCWorkSemaphore.h; sourceTree = "<group>"; };
|
||||
A2D117D21D0B9C51004C229D /* JCAudioInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCAudioInterface.h; sourceTree = "<group>"; };
|
||||
A2D117D31D0B9C51004C229D /* JCAudioWavplayer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCAudioWavplayer.cpp; sourceTree = "<group>"; };
|
||||
A2D117D41D0B9C51004C229D /* JCAudioWavPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCAudioWavPlayer.h; sourceTree = "<group>"; };
|
||||
A2D117D51D0B9C51004C229D /* JCMp3Interface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCMp3Interface.h; sourceTree = "<group>"; };
|
||||
A2D117D81D0B9C51004C229D /* JCWaveInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCWaveInfo.cpp; sourceTree = "<group>"; };
|
||||
A2D117D91D0B9C51004C229D /* JCWaveInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCWaveInfo.h; sourceTree = "<group>"; };
|
||||
A2D117DA1D0B9C51004C229D /* JCWaveParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCWaveParser.cpp; sourceTree = "<group>"; };
|
||||
A2D117DB1D0B9C51004C229D /* JCWaveParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCWaveParser.h; sourceTree = "<group>"; };
|
||||
A2D117EF1D0B9D2C004C229D /* JCFileResManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCFileResManager.cpp; sourceTree = "<group>"; };
|
||||
A2D117F01D0B9D2C004C229D /* JCFileResManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCFileResManager.h; sourceTree = "<group>"; };
|
||||
A2D117F11D0B9D2C004C229D /* JCResManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCResManager.cpp; sourceTree = "<group>"; };
|
||||
A2D117F21D0B9D2C004C229D /* JCResManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCResManager.h; sourceTree = "<group>"; };
|
||||
A2D117F31D0B9D2C004C229D /* JCResource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCResource.cpp; sourceTree = "<group>"; };
|
||||
A2D117F41D0B9D2C004C229D /* JCResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCResource.h; sourceTree = "<group>"; };
|
||||
A2D117F81D0B9D51004C229D /* JCColor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCColor.cpp; sourceTree = "<group>"; };
|
||||
A2D117F91D0B9D51004C229D /* JCColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCColor.h; sourceTree = "<group>"; };
|
||||
A2D117FA1D0B9D51004C229D /* JCCommonMethed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCCommonMethed.cpp; sourceTree = "<group>"; };
|
||||
A2D117FB1D0B9D51004C229D /* JCCommonMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCCommonMethod.h; sourceTree = "<group>"; };
|
||||
A2D117FC1D0B9D51004C229D /* JCCrypto.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCCrypto.cpp; sourceTree = "<group>"; };
|
||||
A2D117FD1D0B9D51004C229D /* JCCrypto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCCrypto.h; sourceTree = "<group>"; };
|
||||
A2D117FE1D0B9D51004C229D /* JCIThreadCmdMgr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCIThreadCmdMgr.h; sourceTree = "<group>"; };
|
||||
A2D117FF1D0B9D51004C229D /* JCJson.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCJson.cpp; sourceTree = "<group>"; };
|
||||
A2D118001D0B9D51004C229D /* JCJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCJson.h; sourceTree = "<group>"; };
|
||||
A2D118011D0B9D51004C229D /* JCLayaUrl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCLayaUrl.cpp; sourceTree = "<group>"; };
|
||||
A2D118021D0B9D51004C229D /* JCLayaUrl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCLayaUrl.h; sourceTree = "<group>"; };
|
||||
A2D118031D0B9D51004C229D /* JCMemorySurvey.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCMemorySurvey.cpp; sourceTree = "<group>"; };
|
||||
A2D118041D0B9D51004C229D /* JCMemorySurvey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCMemorySurvey.h; sourceTree = "<group>"; };
|
||||
A2D118071D0B9D51004C229D /* JCSimpleCRC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCSimpleCRC.h; sourceTree = "<group>"; };
|
||||
A2D118081D0B9D51004C229D /* JCXml.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCXml.cpp; sourceTree = "<group>"; };
|
||||
A2D118091D0B9D51004C229D /* JCXml.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCXml.h; sourceTree = "<group>"; };
|
||||
A2D1180A1D0B9D51004C229D /* JCZipFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCZipFile.cpp; sourceTree = "<group>"; };
|
||||
A2D1180B1D0B9D51004C229D /* JCZipFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCZipFile.h; sourceTree = "<group>"; };
|
||||
A2D1180C1D0B9D51004C229D /* JCZlib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCZlib.cpp; sourceTree = "<group>"; };
|
||||
A2D1180D1D0B9D51004C229D /* JCZlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCZlib.h; sourceTree = "<group>"; };
|
||||
A2D1180E1D0B9D51004C229D /* ListNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ListNode.h; sourceTree = "<group>"; };
|
||||
A2D1180F1D0B9D51004C229D /* Log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Log.cpp; sourceTree = "<group>"; };
|
||||
A2D118101D0B9D51004C229D /* Log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Log.h; sourceTree = "<group>"; };
|
||||
A2D118251D0B9F29004C229D /* JCIGLRender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JCIGLRender.h; path = ../../../source/common/JCIGLRender.h; sourceTree = "<group>"; };
|
||||
A2D11A001D0C2D65004C229D /* JCIniFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JCIniFile.cpp; sourceTree = "<group>"; };
|
||||
A2D11A011D0C2D65004C229D /* JCIniFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JCIniFile.h; sourceTree = "<group>"; };
|
||||
A2D11A1F1D0C3FA0004C229D /* JCIOSFTInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JCIOSFTInterface.h; path = ../../../source/common/JCIOSFTInterface.h; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
8A4296811B6757DB0072C3F7 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
8A42968C1B6757DB0072C3F7 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8A4296901B6757DB0072C3F7 /* libcommon.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
24760D662081C29000594375 /* fontMgr */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
24760D792081C3E500594375 /* JCFontInfo.cpp */,
|
||||
24760D7A2081C3E500594375 /* JCFontInfo.h */,
|
||||
24760D7C2081C3E500594375 /* JCFontManager.cpp */,
|
||||
24760D782081C3E500594375 /* JCFontManager.h */,
|
||||
24760D772081C3E500594375 /* JCFreeTypeRender.cpp */,
|
||||
24760D7D2081C3E500594375 /* JCFreeTypeRender.h */,
|
||||
);
|
||||
name = fontMgr;
|
||||
path = ../../../source/common/fontMgr;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A42967B1B6757DB0072C3F7 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8A42969E1B6758240072C3F7 /* source */,
|
||||
8A4296931B6757DB0072C3F7 /* commonTests */,
|
||||
8A4296851B6757DB0072C3F7 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A4296851B6757DB0072C3F7 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8A4296841B6757DB0072C3F7 /* libcommon.a */,
|
||||
8A42968F1B6757DB0072C3F7 /* commonTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A4296931B6757DB0072C3F7 /* commonTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8A4296941B6757DB0072C3F7 /* Supporting Files */,
|
||||
);
|
||||
path = commonTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A4296941B6757DB0072C3F7 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8A4296951B6757DB0072C3F7 /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A42969E1B6758240072C3F7 /* source */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
24760D662081C29000594375 /* fontMgr */,
|
||||
A2BCECF41E237061003ABF33 /* math */,
|
||||
A2D11A1F1D0C3FA0004C229D /* JCIOSFTInterface.h */,
|
||||
A2D118251D0B9F29004C229D /* JCIGLRender.h */,
|
||||
8A429F921B675CEF0072C3F7 /* buffer */,
|
||||
8A429F951B675CEF0072C3F7 /* downloadCache */,
|
||||
8A429F9E1B675CEF0072C3F7 /* downloadMgr */,
|
||||
8A429FA41B675CEF0072C3F7 /* event */,
|
||||
8A429FAA1B675CEF0072C3F7 /* fileSystem */,
|
||||
8A429FB81B675CEF0072C3F7 /* imageLib */,
|
||||
8A429FC71B675CEF0072C3F7 /* misc */,
|
||||
8A42A0281B675CEF0072C3F7 /* resource */,
|
||||
8A42A0671B675CEF0072C3F7 /* util */,
|
||||
);
|
||||
name = source;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A429F921B675CEF0072C3F7 /* buffer */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A2D117771D0B9B8B004C229D /* JCBuffer.cpp */,
|
||||
A2D117781D0B9B8B004C229D /* JCBuffer.h */,
|
||||
);
|
||||
name = buffer;
|
||||
path = ../../../source/common/buffer;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A429F951B675CEF0072C3F7 /* downloadCache */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A2D1177A1D0B9B9B004C229D /* JCFileSource.h */,
|
||||
A2D1177B1D0B9B9B004C229D /* JCFileTable.cpp */,
|
||||
A2D1177C1D0B9B9B004C229D /* JCFileTable.h */,
|
||||
A2D1177D1D0B9B9B004C229D /* JCIosFileSource.h */,
|
||||
A2D1177E1D0B9B9B004C229D /* JCServerFileCache.cpp */,
|
||||
A2D1177F1D0B9B9B004C229D /* JCServerFileCache.h */,
|
||||
);
|
||||
name = downloadCache;
|
||||
path = ../../../source/common/downloadCache;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A429F9E1B675CEF0072C3F7 /* downloadMgr */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5E68064E1D7D504C002A7575 /* JCHttpHeader.cpp */,
|
||||
5E68064F1D7D504C002A7575 /* JCHttpHeader.h */,
|
||||
A2D117821D0B9BB0004C229D /* JCCurlWrap.cpp */,
|
||||
A2D117831D0B9BB0004C229D /* JCCurlWrap.h */,
|
||||
A2D117841D0B9BB0004C229D /* JCDownloadMgr.cpp */,
|
||||
A2D117851D0B9BB0004C229D /* JCDownloadMgr.h */,
|
||||
);
|
||||
name = downloadMgr;
|
||||
path = ../../../source/common/downloadMgr;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A429FA41B675CEF0072C3F7 /* event */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A2D117881D0B9BBE004C229D /* JCEmitter.cpp */,
|
||||
A2D117891D0B9BBE004C229D /* JCEmitter.h */,
|
||||
A2D1178A1D0B9BBE004C229D /* JCEventBase.cpp */,
|
||||
A2D1178B1D0B9BBE004C229D /* JCEventBase.h */,
|
||||
);
|
||||
name = event;
|
||||
path = ../../../source/common/event;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A429FAA1B675CEF0072C3F7 /* fileSystem */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A2D1178E1D0B9BCF004C229D /* JCFileSystem.cpp */,
|
||||
A2D1178F1D0B9BCF004C229D /* JCFileSystem.h */,
|
||||
);
|
||||
name = fileSystem;
|
||||
path = ../../../source/common/fileSystem;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A429FB81B675CEF0072C3F7 /* imageLib */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9DFB1F571D3632AB006D31AD /* JCGifImg.cpp */,
|
||||
9DFB1F581D3632AB006D31AD /* JCGifImg.h */,
|
||||
A2D117A21D0B9BF5004C229D /* JCImageRW.cpp */,
|
||||
A2D117A31D0B9BF5004C229D /* JCImageRW.h */,
|
||||
A2D117A41D0B9BF5004C229D /* JCJpegImg.cpp */,
|
||||
A2D117A51D0B9BF5004C229D /* JCPngImg.cpp */,
|
||||
);
|
||||
name = imageLib;
|
||||
path = ../../../source/common/imageLib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A429FC71B675CEF0072C3F7 /* misc */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A2D117B21D0B9C20004C229D /* JCCondition.h */,
|
||||
A2D117B31D0B9C20004C229D /* JCGetClockExact.cpp */,
|
||||
A2D117B41D0B9C20004C229D /* JCGetClockExact.h */,
|
||||
A2D117B51D0B9C20004C229D /* JCLayaThreadPool.cpp */,
|
||||
A2D117B61D0B9C20004C229D /* JCLayaThreadPool.h */,
|
||||
A2D117B71D0B9C20004C229D /* JCSingleton.h */,
|
||||
A2D117B81D0B9C20004C229D /* JCThreadPool.h */,
|
||||
A2D117B91D0B9C20004C229D /* JCWorkerThread.cpp */,
|
||||
A2D117BA1D0B9C20004C229D /* JCWorkerThread.h */,
|
||||
A2D117BB1D0B9C20004C229D /* JCWorkSemaphore.cpp */,
|
||||
A2D117BC1D0B9C20004C229D /* JCWorkSemaphore.h */,
|
||||
);
|
||||
name = misc;
|
||||
path = ../../../source/common/misc;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A42A0281B675CEF0072C3F7 /* resource */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A2D117EF1D0B9D2C004C229D /* JCFileResManager.cpp */,
|
||||
A2D117F01D0B9D2C004C229D /* JCFileResManager.h */,
|
||||
A2D117F11D0B9D2C004C229D /* JCResManager.cpp */,
|
||||
A2D117F21D0B9D2C004C229D /* JCResManager.h */,
|
||||
A2D117F31D0B9D2C004C229D /* JCResource.cpp */,
|
||||
A2D117F41D0B9D2C004C229D /* JCResource.h */,
|
||||
8A42A0311B675CEF0072C3F7 /* Audio */,
|
||||
);
|
||||
name = resource;
|
||||
path = ../../../source/common/resource;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A42A0311B675CEF0072C3F7 /* Audio */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9DFB1EDC1D34EB33006D31AD /* JCOggParser.cpp */,
|
||||
9DFB1EDD1D34EB33006D31AD /* JCOggParser.h */,
|
||||
A2D117D21D0B9C51004C229D /* JCAudioInterface.h */,
|
||||
A2D117D31D0B9C51004C229D /* JCAudioWavplayer.cpp */,
|
||||
A2D117D41D0B9C51004C229D /* JCAudioWavPlayer.h */,
|
||||
A2D117D51D0B9C51004C229D /* JCMp3Interface.h */,
|
||||
A2D117D81D0B9C51004C229D /* JCWaveInfo.cpp */,
|
||||
A2D117D91D0B9C51004C229D /* JCWaveInfo.h */,
|
||||
A2D117DA1D0B9C51004C229D /* JCWaveParser.cpp */,
|
||||
A2D117DB1D0B9C51004C229D /* JCWaveParser.h */,
|
||||
);
|
||||
path = Audio;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A42A0671B675CEF0072C3F7 /* util */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A242E3E91D813D420013CCF0 /* JCFlog.cpp */,
|
||||
A242E3EA1D813D420013CCF0 /* JCFlog.h */,
|
||||
A2D11A001D0C2D65004C229D /* JCIniFile.cpp */,
|
||||
A2D11A011D0C2D65004C229D /* JCIniFile.h */,
|
||||
A2D117F81D0B9D51004C229D /* JCColor.cpp */,
|
||||
A2D117F91D0B9D51004C229D /* JCColor.h */,
|
||||
A2D117FA1D0B9D51004C229D /* JCCommonMethed.cpp */,
|
||||
A2D117FB1D0B9D51004C229D /* JCCommonMethod.h */,
|
||||
A2D117FC1D0B9D51004C229D /* JCCrypto.cpp */,
|
||||
A2D117FD1D0B9D51004C229D /* JCCrypto.h */,
|
||||
A2D117FE1D0B9D51004C229D /* JCIThreadCmdMgr.h */,
|
||||
A2D117FF1D0B9D51004C229D /* JCJson.cpp */,
|
||||
A2D118001D0B9D51004C229D /* JCJson.h */,
|
||||
A2D118011D0B9D51004C229D /* JCLayaUrl.cpp */,
|
||||
A2D118021D0B9D51004C229D /* JCLayaUrl.h */,
|
||||
A2D118031D0B9D51004C229D /* JCMemorySurvey.cpp */,
|
||||
A2D118041D0B9D51004C229D /* JCMemorySurvey.h */,
|
||||
A2D118071D0B9D51004C229D /* JCSimpleCRC.h */,
|
||||
A2D118081D0B9D51004C229D /* JCXml.cpp */,
|
||||
A2D118091D0B9D51004C229D /* JCXml.h */,
|
||||
A2D1180A1D0B9D51004C229D /* JCZipFile.cpp */,
|
||||
A2D1180B1D0B9D51004C229D /* JCZipFile.h */,
|
||||
A2D1180C1D0B9D51004C229D /* JCZlib.cpp */,
|
||||
A2D1180D1D0B9D51004C229D /* JCZlib.h */,
|
||||
A2D1180E1D0B9D51004C229D /* ListNode.h */,
|
||||
A2D1180F1D0B9D51004C229D /* Log.cpp */,
|
||||
A2D118101D0B9D51004C229D /* Log.h */,
|
||||
8A42A0861B675CEF0072C3F7 /* rapidxml */,
|
||||
);
|
||||
name = util;
|
||||
path = ../../../source/common/util;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8A42A0861B675CEF0072C3F7 /* rapidxml */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8A42A0871B675CEF0072C3F7 /* rapidxml.hpp */,
|
||||
8A42A0881B675CEF0072C3F7 /* rapidxml_iterators.hpp */,
|
||||
8A42A0891B675CEF0072C3F7 /* rapidxml_print.hpp */,
|
||||
8A42A08A1B675CEF0072C3F7 /* rapidxml_utils.hpp */,
|
||||
);
|
||||
path = rapidxml;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A2BCECF41E237061003ABF33 /* math */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A27D92532106D9BC00CD920F /* Matrix32.cpp */,
|
||||
A27D92572106D9BD00CD920F /* Matrix32.h */,
|
||||
);
|
||||
name = math;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8A4296831B6757DB0072C3F7 /* common */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 8A4296981B6757DB0072C3F7 /* Build configuration list for PBXNativeTarget "common" */;
|
||||
buildPhases = (
|
||||
8A4296801B6757DB0072C3F7 /* Sources */,
|
||||
8A4296811B6757DB0072C3F7 /* Frameworks */,
|
||||
8A4296821B6757DB0072C3F7 /* CopyFiles */,
|
||||
A281055E1F735E7E0094C3CC /* ShellScript */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = common;
|
||||
productName = common;
|
||||
productReference = 8A4296841B6757DB0072C3F7 /* libcommon.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
8A42968E1B6757DB0072C3F7 /* commonTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 8A42969B1B6757DB0072C3F7 /* Build configuration list for PBXNativeTarget "commonTests" */;
|
||||
buildPhases = (
|
||||
8A42968B1B6757DB0072C3F7 /* Sources */,
|
||||
8A42968C1B6757DB0072C3F7 /* Frameworks */,
|
||||
8A42968D1B6757DB0072C3F7 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
8A4296921B6757DB0072C3F7 /* PBXTargetDependency */,
|
||||
);
|
||||
name = commonTests;
|
||||
productName = commonTests;
|
||||
productReference = 8A42968F1B6757DB0072C3F7 /* commonTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
8A42967C1B6757DB0072C3F7 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0640;
|
||||
ORGANIZATIONNAME = Laya.layabox.conch;
|
||||
TargetAttributes = {
|
||||
8A4296831B6757DB0072C3F7 = {
|
||||
CreatedOnToolsVersion = 6.4;
|
||||
};
|
||||
8A42968E1B6757DB0072C3F7 = {
|
||||
CreatedOnToolsVersion = 6.4;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 8A42967F1B6757DB0072C3F7 /* Build configuration list for PBXProject "common" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
English,
|
||||
en,
|
||||
);
|
||||
mainGroup = 8A42967B1B6757DB0072C3F7;
|
||||
productRefGroup = 8A4296851B6757DB0072C3F7 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8A4296831B6757DB0072C3F7 /* common */,
|
||||
8A42968E1B6757DB0072C3F7 /* commonTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
8A42968D1B6757DB0072C3F7 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
A281055E1F735E7E0094C3CC /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "chmod u+x $SRCROOT/../../copyLib.sh\n$SRCROOT/../../copyLib.sh\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8A4296801B6757DB0072C3F7 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A2D117F51D0B9D2C004C229D /* JCFileResManager.cpp in Sources */,
|
||||
A2D117F61D0B9D2C004C229D /* JCResManager.cpp in Sources */,
|
||||
A2D1181B1D0B9D51004C229D /* Log.cpp in Sources */,
|
||||
A2D117A71D0B9BF5004C229D /* JCJpegImg.cpp in Sources */,
|
||||
A2D11A021D0C2D65004C229D /* JCIniFile.cpp in Sources */,
|
||||
A2D118191D0B9D51004C229D /* JCZipFile.cpp in Sources */,
|
||||
A2D118141D0B9D51004C229D /* JCJson.cpp in Sources */,
|
||||
A2D117DF1D0B9C51004C229D /* JCWaveParser.cpp in Sources */,
|
||||
A2D117A81D0B9BF5004C229D /* JCPngImg.cpp in Sources */,
|
||||
A2D1178D1D0B9BBE004C229D /* JCEventBase.cpp in Sources */,
|
||||
5E6806501D7D504C002A7575 /* JCHttpHeader.cpp in Sources */,
|
||||
A2D117901D0B9BCF004C229D /* JCFileSystem.cpp in Sources */,
|
||||
A2D117F71D0B9D2C004C229D /* JCResource.cpp in Sources */,
|
||||
A2D1178C1D0B9BBE004C229D /* JCEmitter.cpp in Sources */,
|
||||
A2D118151D0B9D51004C229D /* JCLayaUrl.cpp in Sources */,
|
||||
A27D925B2106D9BD00CD920F /* Matrix32.cpp in Sources */,
|
||||
A2D117BF1D0B9C20004C229D /* JCWorkerThread.cpp in Sources */,
|
||||
9DFB1EDE1D34EB33006D31AD /* JCOggParser.cpp in Sources */,
|
||||
A2D117801D0B9B9B004C229D /* JCFileTable.cpp in Sources */,
|
||||
A2D118181D0B9D51004C229D /* JCXml.cpp in Sources */,
|
||||
A2D1181A1D0B9D51004C229D /* JCZlib.cpp in Sources */,
|
||||
24760D812081C3E600594375 /* JCFontManager.cpp in Sources */,
|
||||
A2D117DC1D0B9C51004C229D /* JCAudioWavplayer.cpp in Sources */,
|
||||
A2D117BD1D0B9C20004C229D /* JCGetClockExact.cpp in Sources */,
|
||||
A2D117C01D0B9C20004C229D /* JCWorkSemaphore.cpp in Sources */,
|
||||
A2D117DE1D0B9C51004C229D /* JCWaveInfo.cpp in Sources */,
|
||||
24760D802081C3E600594375 /* JCFontInfo.cpp in Sources */,
|
||||
A2D118121D0B9D51004C229D /* JCCommonMethed.cpp in Sources */,
|
||||
A2D117871D0B9BB0004C229D /* JCDownloadMgr.cpp in Sources */,
|
||||
A2D118161D0B9D51004C229D /* JCMemorySurvey.cpp in Sources */,
|
||||
A242E3EB1D813D420013CCF0 /* JCFlog.cpp in Sources */,
|
||||
9DFB1F591D3632AB006D31AD /* JCGifImg.cpp in Sources */,
|
||||
A2D118111D0B9D51004C229D /* JCColor.cpp in Sources */,
|
||||
A2D117861D0B9BB0004C229D /* JCCurlWrap.cpp in Sources */,
|
||||
A2D117BE1D0B9C20004C229D /* JCLayaThreadPool.cpp in Sources */,
|
||||
A2D117791D0B9B8B004C229D /* JCBuffer.cpp in Sources */,
|
||||
A2D117811D0B9B9B004C229D /* JCServerFileCache.cpp in Sources */,
|
||||
A2D117A61D0B9BF5004C229D /* JCImageRW.cpp in Sources */,
|
||||
A2D118131D0B9D51004C229D /* JCCrypto.cpp in Sources */,
|
||||
24760D7F2081C3E600594375 /* JCFreeTypeRender.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
8A42968B1B6757DB0072C3F7 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
8A4296921B6757DB0072C3F7 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 8A4296831B6757DB0072C3F7 /* common */;
|
||||
targetProxy = 8A4296911B6757DB0072C3F7 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
8A4296961B6757DB0072C3F7 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "";
|
||||
SDKROOT = iphoneos;
|
||||
SYMROOT = build;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8A4296971B6757DB0072C3F7 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_CFLAGS = "";
|
||||
SDKROOT = iphoneos;
|
||||
SYMROOT = build;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
8A4296991B6757DB0072C3F7 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
OTHER_CFLAGS = (
|
||||
"-I../../../../ThirdParty/freetype/include/ios",
|
||||
"-I../../../../ThirdParty/jpeg/include/ios",
|
||||
"-I../../../../ThirdParty/zip/include/ios",
|
||||
"-I../../../../ThirdParty/curl/include/ios",
|
||||
"-I../../../../ThirdParty/png/include/ios",
|
||||
"-I../../../../ThirdParty/ogg/include/ios",
|
||||
"-I../../../../ThirdParty/openssl/include/ios",
|
||||
"-I../../../../ThirdParty/zlib/include/ios",
|
||||
);
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SYMROOT = build;
|
||||
VALID_ARCHS = "arm64 armv7 i386 x86_64";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8A42969A1B6757DB0072C3F7 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
OTHER_CFLAGS = (
|
||||
"-I../../../../ThirdParty/freetype/include/ios",
|
||||
"-I../../../../ThirdParty/jpeg/include/ios",
|
||||
"-I../../../../ThirdParty/zip/include/ios",
|
||||
"-I../../../../ThirdParty/curl/include/ios",
|
||||
"-I../../../../ThirdParty/png/include/ios",
|
||||
"-I../../../../ThirdParty/ogg/include/ios",
|
||||
"-I../../../../ThirdParty/openssl/include/ios",
|
||||
"-I../../../../ThirdParty/zlib/include/ios",
|
||||
);
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SYMROOT = build;
|
||||
VALID_ARCHS = "arm64 armv7 i386 x86_64";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
8A42969C1B6757DB0072C3F7 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = commonTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8A42969D1B6757DB0072C3F7 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = commonTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
8A42967F1B6757DB0072C3F7 /* Build configuration list for PBXProject "common" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8A4296961B6757DB0072C3F7 /* Debug */,
|
||||
8A4296971B6757DB0072C3F7 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
8A4296981B6757DB0072C3F7 /* Build configuration list for PBXNativeTarget "common" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8A4296991B6757DB0072C3F7 /* Debug */,
|
||||
8A42969A1B6757DB0072C3F7 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
8A42969B1B6757DB0072C3F7 /* Build configuration list for PBXNativeTarget "commonTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8A42969C1B6757DB0072C3F7 /* Debug */,
|
||||
8A42969D1B6757DB0072C3F7 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 8A42967C1B6757DB0072C3F7 /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:common.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// common.h
|
||||
// common
|
||||
//
|
||||
// Created by joychina on 15/7/28.
|
||||
// Copyright (c) 2015年 Laya.layabox.conch. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface common : NSObject
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// common.m
|
||||
// common
|
||||
//
|
||||
// Created by joychina on 15/7/28.
|
||||
// Copyright (c) 2015年 Laya.layabox.conch. All rights reserved.
|
||||
//
|
||||
|
||||
#import "common.h"
|
||||
|
||||
@implementation common
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>layabox.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,2 @@
|
||||
Debug
|
||||
Release
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2012
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common.vcxproj", "{ABB51704-6CEB-42B4-9B14-9723FBBC08B2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{ABB51704-6CEB-42B4-9B14-9723FBBC08B2}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{ABB51704-6CEB-42B4-9B14-9723FBBC08B2}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{ABB51704-6CEB-42B4-9B14-9723FBBC08B2}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{ABB51704-6CEB-42B4-9B14-9723FBBC08B2}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,186 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\source\common\buffer\JCBuffer.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\downloadCache\JCFileTable.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\downloadCache\JCServerFileCache.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\downloadMgr\JCCurlWrap.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\downloadMgr\JCDownloadMgr.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\downloadMgr\JCHttpHeader.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\event\JCEmitter.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\event\JCEventBase.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\fileSystem\JCFileSystem.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\fontMgr\JCFontInfo.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\fontMgr\JCFontManager.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\fontMgr\JCFreeTypeRender.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\imageLib\JCGifImg.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\imageLib\JCImageRW.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\imageLib\JCJpegImg.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\imageLib\JCPngImg.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\math\Matrix32.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\misc\JCGetClockExact.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\misc\JCLayaThreadPool.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\misc\JCWorkerThread.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\misc\JCWorkSemaphore.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\resource\Audio\JCAudioWavplayer.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\resource\Audio\JCOggParser.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\resource\Audio\JCWaveInfo.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\resource\Audio\JCWaveParser.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\resource\JCFileResManager.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\resource\JCResource.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\resource\JCResManager.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\util\JCCrypto.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\util\JCColor.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\util\JCCommonMethed.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\util\JCFlog.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\util\JCIniFile.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\util\JCJson.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\util\JCXml.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\util\JCMemorySurvey.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\util\JCZipFile.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\util\JCLayaUrl.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\util\JCZlib.cpp" />
|
||||
<ClCompile Include="..\..\..\source\common\util\Log.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\source\common\buffer\JCBuffer.h" />
|
||||
<ClInclude Include="..\..\..\source\common\downloadCache\JCFileSource.h" />
|
||||
<ClInclude Include="..\..\..\source\common\downloadCache\JCFileTable.h" />
|
||||
<ClInclude Include="..\..\..\source\common\downloadCache\JCIosFileSource.h" />
|
||||
<ClInclude Include="..\..\..\source\common\downloadCache\JCServerFileCache.h" />
|
||||
<ClInclude Include="..\..\..\source\common\downloadMgr\JCCurlWrap.h" />
|
||||
<ClInclude Include="..\..\..\source\common\downloadMgr\JCDownloadMgr.h" />
|
||||
<ClInclude Include="..\..\..\source\common\downloadMgr\JCHttpHeader.h" />
|
||||
<ClInclude Include="..\..\..\source\common\event\JCEmitter.h" />
|
||||
<ClInclude Include="..\..\..\source\common\event\JCEventBase.h" />
|
||||
<ClInclude Include="..\..\..\source\common\fileSystem\JCFileSystem.h" />
|
||||
<ClInclude Include="..\..\..\source\common\fontMgr\JCFontInfo.h" />
|
||||
<ClInclude Include="..\..\..\source\common\fontMgr\JCFontManager.h" />
|
||||
<ClInclude Include="..\..\..\source\common\fontMgr\JCFreeTypeRender.h" />
|
||||
<ClInclude Include="..\..\..\source\common\imageLib\JCGifImg.h" />
|
||||
<ClInclude Include="..\..\..\source\common\imageLib\JCImageRW.h" />
|
||||
<ClInclude Include="..\..\..\source\common\JCIGLRender.h" />
|
||||
<ClInclude Include="..\..\..\source\common\math\Matrix32.h" />
|
||||
<ClInclude Include="..\..\..\source\common\misc\JCGetClockExact.h" />
|
||||
<ClInclude Include="..\..\..\source\common\misc\JCCondition.h" />
|
||||
<ClInclude Include="..\..\..\source\common\misc\JCLayaThreadPool.h" />
|
||||
<ClInclude Include="..\..\..\source\common\misc\JCThreadPool.h" />
|
||||
<ClInclude Include="..\..\..\source\common\misc\JCSingleton.h" />
|
||||
<ClInclude Include="..\..\..\source\common\misc\JCWorkerThread.h" />
|
||||
<ClInclude Include="..\..\..\source\common\misc\JCWorkSemaphore.h" />
|
||||
<ClInclude Include="..\..\..\source\common\JCIOSFTInterface.h" />
|
||||
<ClInclude Include="..\..\..\source\common\resource\Audio\JCAudioInterface.h" />
|
||||
<ClInclude Include="..\..\..\source\common\resource\Audio\JCAudioWavPlayer.h" />
|
||||
<ClInclude Include="..\..\..\source\common\resource\Audio\JCMp3Interface.h" />
|
||||
<ClInclude Include="..\..\..\source\common\resource\Audio\JCOggParser.h" />
|
||||
<ClInclude Include="..\..\..\source\common\resource\Audio\JCWaveInfo.h" />
|
||||
<ClInclude Include="..\..\..\source\common\resource\Audio\JCWaveParser.h" />
|
||||
<ClInclude Include="..\..\..\source\common\resource\JCFileResManager.h" />
|
||||
<ClInclude Include="..\..\..\source\common\resource\JCResource.h" />
|
||||
<ClInclude Include="..\..\..\source\common\resource\JCResManager.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\JCCrypto.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\JCColor.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\JCCommonMethod.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\JCFlog.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\JCIniFile.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\JCIThreadCmdMgr.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\JCJson.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\JCSimpleCRC.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\JCXml.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\JCMemorySurvey.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\JCZipFile.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\JCLayaUrl.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\JCZlib.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\ListNode.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\Log.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\rapidxml\rapidxml.hpp" />
|
||||
<ClInclude Include="..\..\..\source\common\util\rapidxml\rapidxml_iterators.hpp" />
|
||||
<ClInclude Include="..\..\..\source\common\util\rapidxml\rapidxml_print.hpp" />
|
||||
<ClInclude Include="..\..\..\source\common\util\rapidxml\rapidxml_utils.hpp" />
|
||||
<ClInclude Include="..\..\..\source\common\util\RefObject.h" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{ABB51704-6CEB-42B4-9B14-9723FBBC08B2}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>common</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<OutDir>..\..\..\libs\win32</OutDir>
|
||||
<TargetName>$(ProjectName)_d</TargetName>
|
||||
<IntDir>$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<OutDir>..\..\..\libs\win32</OutDir>
|
||||
<IntDir>$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;GLM_FORCE_DEPTH_ZERO_TO_ONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\ThirdParty\curl\include\win32;..\..\..\..\ThirdParty\angle\include;..\..\..\..\ThirdParty\png\include\win32;..\..\..\..\ThirdParty\jpeg\include\win32;..\..\..\..\ThirdParty\zlib\include\win32;..\..\..\..\ThirdParty\freetype\include\win32;..\..\..\..\ThirdParty\zip\include\win32;..\..\..\..\ThirdParty\openal\include;..\..\..\..\ThirdParty\ogg\include\win32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;GLM_FORCE_DEPTH_ZERO_TO_ONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\ThirdParty\curl\include\win32;..\..\..\..\ThirdParty\angle\include;..\..\..\..\ThirdParty\png\include\win32;..\..\..\..\ThirdParty\jpeg\include\win32;..\..\..\..\ThirdParty\zlib\include\win32;..\..\..\..\ThirdParty\freetype\include\win32;..\..\..\..\ThirdParty\zip\include\win32;..\..\..\..\ThirdParty\openal\include;..\..\..\..\ThirdParty\ogg\include\win32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,329 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="downloadCache">
|
||||
<UniqueIdentifier>{3f5c5bd7-853c-4636-bedc-7e8ff9a4981b}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="downloadMgr">
|
||||
<UniqueIdentifier>{e8bc0b24-fc64-48b1-a55d-1222f6d75240}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="event">
|
||||
<UniqueIdentifier>{7691b011-e1f7-4542-9ff8-49317e1bec3b}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="fileSystem">
|
||||
<UniqueIdentifier>{c4c37ed1-ce6e-464d-bf57-819c5c96e0f7}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="imageLib">
|
||||
<UniqueIdentifier>{11fb53b7-e1f4-4d12-aabc-7ded01f7c2ac}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="misc">
|
||||
<UniqueIdentifier>{c63d89be-0324-4d1d-a79d-4ce56587d188}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="resource">
|
||||
<UniqueIdentifier>{179f548f-8778-40d9-95e6-2ee358baaab1}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="util">
|
||||
<UniqueIdentifier>{83114a8d-9f83-4091-aaa6-e43fa8bdf490}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="util\rapidxml">
|
||||
<UniqueIdentifier>{afd6de01-1914-429f-8e94-6b7261c7f62d}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="resource\Audio">
|
||||
<UniqueIdentifier>{d06354d7-0d5f-47de-a946-d60f48125376}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="fontMgr">
|
||||
<UniqueIdentifier>{063b01b0-16d8-4357-be08-b3cb4a161d3e}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="math">
|
||||
<UniqueIdentifier>{6008e6d8-9cc0-45e5-8b31-96d9ed790acf}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="buffer">
|
||||
<UniqueIdentifier>{fe27facc-9a04-4383-8ef2-6b5dd97916ec}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\source\common\util\JCJson.cpp">
|
||||
<Filter>util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\util\JCXml.cpp">
|
||||
<Filter>util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\resource\Audio\JCAudioWavplayer.cpp">
|
||||
<Filter>resource\Audio</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\resource\Audio\JCWaveInfo.cpp">
|
||||
<Filter>resource\Audio</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\resource\Audio\JCWaveParser.cpp">
|
||||
<Filter>resource\Audio</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\resource\Audio\JCOggParser.cpp">
|
||||
<Filter>resource\Audio</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\util\JCColor.cpp">
|
||||
<Filter>util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\downloadCache\JCFileTable.cpp">
|
||||
<Filter>downloadCache</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\downloadCache\JCServerFileCache.cpp">
|
||||
<Filter>downloadCache</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\fileSystem\JCFileSystem.cpp">
|
||||
<Filter>fileSystem</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\util\JCMemorySurvey.cpp">
|
||||
<Filter>util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\downloadMgr\JCDownloadMgr.cpp">
|
||||
<Filter>downloadMgr</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\event\JCEmitter.cpp">
|
||||
<Filter>event</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\event\JCEventBase.cpp">
|
||||
<Filter>event</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\imageLib\JCImageRW.cpp">
|
||||
<Filter>imageLib</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\imageLib\JCJpegImg.cpp">
|
||||
<Filter>imageLib</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\imageLib\JCPngImg.cpp">
|
||||
<Filter>imageLib</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\resource\JCFileResManager.cpp">
|
||||
<Filter>resource</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\misc\JCWorkSemaphore.cpp">
|
||||
<Filter>misc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\misc\JCWorkerThread.cpp">
|
||||
<Filter>misc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\util\JCCommonMethed.cpp">
|
||||
<Filter>util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\util\JCZipFile.cpp">
|
||||
<Filter>util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\util\JCLayaUrl.cpp">
|
||||
<Filter>util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\misc\JCLayaThreadPool.cpp">
|
||||
<Filter>misc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\resource\JCResource.cpp">
|
||||
<Filter>resource</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\misc\JCGetClockExact.cpp">
|
||||
<Filter>misc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\resource\JCResManager.cpp">
|
||||
<Filter>resource</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\downloadMgr\JCCurlWrap.cpp">
|
||||
<Filter>downloadMgr</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\util\JCCrypto.cpp">
|
||||
<Filter>util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\util\Log.cpp">
|
||||
<Filter>util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\util\JCZlib.cpp">
|
||||
<Filter>util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\util\JCIniFile.cpp">
|
||||
<Filter>util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\imageLib\JCGifImg.cpp">
|
||||
<Filter>imageLib</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\downloadMgr\JCHttpHeader.cpp">
|
||||
<Filter>downloadMgr</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\util\JCFlog.cpp">
|
||||
<Filter>util</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\fontMgr\JCFontInfo.cpp">
|
||||
<Filter>fontMgr</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\fontMgr\JCFontManager.cpp">
|
||||
<Filter>fontMgr</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\fontMgr\JCFreeTypeRender.cpp">
|
||||
<Filter>fontMgr</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\math\Matrix32.cpp">
|
||||
<Filter>math</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\source\common\buffer\JCBuffer.cpp">
|
||||
<Filter>buffer</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\source\common\util\JCJson.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\JCXml.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\Log.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\rapidxml\rapidxml.hpp">
|
||||
<Filter>util\rapidxml</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\rapidxml\rapidxml_iterators.hpp">
|
||||
<Filter>util\rapidxml</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\rapidxml\rapidxml_print.hpp">
|
||||
<Filter>util\rapidxml</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\rapidxml\rapidxml_utils.hpp">
|
||||
<Filter>util\rapidxml</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\resource\Audio\JCAudioInterface.h">
|
||||
<Filter>resource\Audio</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\resource\Audio\JCAudioWavPlayer.h">
|
||||
<Filter>resource\Audio</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\resource\Audio\JCMp3Interface.h">
|
||||
<Filter>resource\Audio</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\resource\Audio\JCWaveInfo.h">
|
||||
<Filter>resource\Audio</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\resource\Audio\JCWaveParser.h">
|
||||
<Filter>resource\Audio</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\resource\Audio\JCOggParser.h">
|
||||
<Filter>resource\Audio</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\JCColor.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\downloadCache\JCFileSource.h">
|
||||
<Filter>downloadCache</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\downloadCache\JCFileTable.h">
|
||||
<Filter>downloadCache</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\downloadCache\JCIosFileSource.h">
|
||||
<Filter>downloadCache</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\downloadCache\JCServerFileCache.h">
|
||||
<Filter>downloadCache</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\fileSystem\JCFileSystem.h">
|
||||
<Filter>fileSystem</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\JCMemorySurvey.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\downloadMgr\JCDownloadMgr.h">
|
||||
<Filter>downloadMgr</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\event\JCEmitter.h">
|
||||
<Filter>event</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\event\JCEventBase.h">
|
||||
<Filter>event</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\imageLib\JCImageRW.h">
|
||||
<Filter>imageLib</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\resource\JCFileResManager.h">
|
||||
<Filter>resource</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\misc\JCWorkSemaphore.h">
|
||||
<Filter>misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\misc\JCWorkerThread.h">
|
||||
<Filter>misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\JCCommonMethod.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\JCZipFile.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\JCLayaUrl.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\misc\JCCondition.h">
|
||||
<Filter>misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\misc\JCLayaThreadPool.h">
|
||||
<Filter>misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\misc\JCSingleton.h">
|
||||
<Filter>misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\ListNode.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\JCSimpleCRC.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\misc\JCThreadPool.h">
|
||||
<Filter>misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\resource\JCResource.h">
|
||||
<Filter>resource</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\misc\JCGetClockExact.h">
|
||||
<Filter>misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\resource\JCResManager.h">
|
||||
<Filter>resource</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\downloadMgr\JCCurlWrap.h">
|
||||
<Filter>downloadMgr</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\JCCrypto.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\JCZlib.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\JCIThreadCmdMgr.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\JCIGLRender.h" />
|
||||
<ClInclude Include="..\..\..\source\common\util\JCIniFile.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\JCIOSFTInterface.h" />
|
||||
<ClInclude Include="..\..\..\source\common\imageLib\JCGifImg.h">
|
||||
<Filter>imageLib</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\downloadMgr\JCHttpHeader.h">
|
||||
<Filter>downloadMgr</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\JCFlog.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\util\RefObject.h">
|
||||
<Filter>util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\fontMgr\JCFontInfo.h">
|
||||
<Filter>fontMgr</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\fontMgr\JCFontManager.h">
|
||||
<Filter>fontMgr</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\fontMgr\JCFreeTypeRender.h">
|
||||
<Filter>fontMgr</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\math\Matrix32.h">
|
||||
<Filter>math</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\source\common\buffer\JCBuffer.h">
|
||||
<Filter>buffer</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,2 @@
|
||||
local.properties
|
||||
*.bin
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,43 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
buildToolsVersion "28.0.0"
|
||||
useLibrary 'org.apache.http.legacy'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.layabox.conch6"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 28
|
||||
versionCode 17
|
||||
versionName "release-2.9.0"
|
||||
}
|
||||
sourceSets.main{
|
||||
jniLibs.srcDir 'libs'
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
debug {
|
||||
debuggable true
|
||||
jniDebuggable true
|
||||
renderscriptDebuggable true
|
||||
}
|
||||
}
|
||||
/* externalNativeBuild {
|
||||
ndkBuild {
|
||||
path '../../proj.android/jni/Android.mk'
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
testImplementation 'junit:junit:4.12'
|
||||
implementation project(':conch5')
|
||||
implementation group: 'com.google.zxing', name: 'core', version: '3.3.1'
|
||||
implementation 'com.journeyapps:zxing-android-embedded:3.3.0'
|
||||
implementation 'com.android.support:appcompat-v7:25.3.1'
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in D:\sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
-dontusemixedcaseclassnames
|
||||
-keepattributes *Annotation*
|
||||
|
||||
-keep public class * extends android.app.Activity
|
||||
-keep public class * extends android.app.Application
|
||||
-keep public class * extends android.app.Service
|
||||
-keep public class * extends android.content.BroadcastReceiver
|
||||
-keep public class * extends android.content.ContentProvider
|
||||
-keep public class * extends android.app.backup.BackupAgentHelper
|
||||
-keep public class * extends android.preference.Preference
|
||||
-keep public class com.android.vending.licensing.ILicensingService
|
||||
|
||||
|
||||
-keep class layaair.game.PlatformInterface.** {*;}
|
||||
-keep class layaair.game.wrapper.** {*;}
|
||||
-keep class layaair.game.device.DevID {*;}
|
||||
-keep class layaair.game.browser.ConchJNI{*;}
|
||||
-keep class layaair.game.browser.ExportJavaFunction {*;}
|
||||
-keep class layaair.game.utility.ProcessInfo {*;}
|
||||
-keep class layaair.game.utility.LayaAudioMusic {*;}
|
||||
-keep class layaair.game.Notifycation.LayaNotifyManager {*;}
|
||||
-keep class layaair.game.conch.ILayaEventListener {*;}
|
||||
-keep class layaair.game.conch.ILayaGameEgine {*;}
|
||||
-keep class layaair.game.conch.LayaConch5 {*;}
|
||||
|
||||
|
||||
-keepclasseswithmembernames class *{
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
-keepclasseswithmembers class *{
|
||||
public <init>(android.content.Context,android.util.AttributeSet);
|
||||
}
|
||||
|
||||
-keepclasseswithmembers class *{
|
||||
public <init>(android.content.Context,android.util.AttributeSet, int);
|
||||
}
|
||||
|
||||
-keepclassmembers class * extends android.app.Activity{
|
||||
public void *(android.view.View);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.layabox.conch6;
|
||||
|
||||
import android.app.Application;
|
||||
import android.test.ApplicationTestCase;
|
||||
|
||||
/**
|
||||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
||||
*/
|
||||
public class ApplicationTest extends ApplicationTestCase<Application> {
|
||||
public ApplicationTest() {
|
||||
super(Application.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.layabox.conch6">
|
||||
|
||||
<!-- Keeps the processor from sleeping when a message is received. -->
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
<application
|
||||
android:usesCleartextTraffic="true"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar">
|
||||
<!-- 竖portrait 横landscape sensorLandscape sensorPortrait -->
|
||||
<activity
|
||||
android:name="layaair.game.browser.MainActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
|
||||
android:label="@string/app_name" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="layabox" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="layaair.game.browser.ScanActivity"
|
||||
android:clearTaskOnLaunch="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:stateNotNeeded="true"
|
||||
android:theme="@style/AppCompatCaptureTheme"
|
||||
android:windowSoftInputMode="stateAlwaysHidden"></activity>
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
android:authorities="com.layabox.conch6.fileprovider"
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
<uses-library
|
||||
android:name="org.apache.http.legacy"
|
||||
android:required="false" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,8 @@
|
||||
IsHandleUpdateAPK=1
|
||||
ApkUpdateUrl=https://www.layabox.com/layanative2.0/apk/update/conch-layaair/version.xml
|
||||
UpdateDownloadPath=mnt/sdcard
|
||||
UpdateAPKFileName=autoupdate.apk
|
||||
CheckNetwork=1
|
||||
ThreadMode=1
|
||||
JSDebugPort=5959
|
||||
JSDebugMode=1
|
||||
@@ -0,0 +1,115 @@
|
||||
class loadingView
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this.sOS = conchConfig.getOS();
|
||||
if (this.sOS == "Conch-ios")
|
||||
{
|
||||
this.bridge = PlatformClass.createClass("JSBridge");
|
||||
}
|
||||
else if (this.sOS == "Conch-android")
|
||||
{
|
||||
this.bridge = PlatformClass.createClass("layaair.game.browser.JSBridge");
|
||||
}
|
||||
}
|
||||
set loadingAutoClose(value)
|
||||
{
|
||||
this._loadingAutoClose = value;
|
||||
}
|
||||
get loadingAutoClose()
|
||||
{
|
||||
return this._loadingAutoClose;
|
||||
}
|
||||
set showTextInfo(value)
|
||||
{
|
||||
this._showTextInfo = value;
|
||||
if(this.bridge)
|
||||
{
|
||||
if (this.sOS == "Conch-ios")
|
||||
{
|
||||
this.bridge.call("showTextInfo:",value);
|
||||
}
|
||||
else if(this.sOS == "Conch-android")
|
||||
{
|
||||
this.bridge.call("showTextInfo",value);
|
||||
}
|
||||
}
|
||||
}
|
||||
get showTextInfo()
|
||||
{
|
||||
return this._showTextInfo;
|
||||
}
|
||||
bgColor(value)
|
||||
{
|
||||
if(this.bridge)
|
||||
{
|
||||
if (this.sOS == "Conch-ios")
|
||||
{
|
||||
this.bridge.call("bgColor:",value);
|
||||
}
|
||||
else if(this.sOS == "Conch-android")
|
||||
{
|
||||
this.bridge.call("bgColor",value);
|
||||
}
|
||||
}
|
||||
}
|
||||
setFontColor(value)
|
||||
{
|
||||
if(this.bridge)
|
||||
{
|
||||
if (this.sOS == "Conch-ios")
|
||||
{
|
||||
this.bridge.call("setFontColor:",value);
|
||||
}
|
||||
else if(this.sOS == "Conch-android")
|
||||
{
|
||||
this.bridge.call("setFontColor",value);
|
||||
}
|
||||
}
|
||||
}
|
||||
setTips(value)
|
||||
{
|
||||
if(this.bridge)
|
||||
{
|
||||
if (this.sOS == "Conch-ios")
|
||||
{
|
||||
this.bridge.call("setTips:",value);
|
||||
}
|
||||
else if(this.sOS == "Conch-android")
|
||||
{
|
||||
this.bridge.call("setTips",value);
|
||||
}
|
||||
}
|
||||
}
|
||||
loading(value)
|
||||
{
|
||||
if(this.bridge)
|
||||
{
|
||||
if (this.sOS == "Conch-ios")
|
||||
{
|
||||
this.bridge.call("loading:",value);
|
||||
}
|
||||
else if(this.sOS == "Conch-android")
|
||||
{
|
||||
this.bridge.call("loading",value);
|
||||
}
|
||||
}
|
||||
}
|
||||
hideLoadingView()
|
||||
{
|
||||
this.bridge.call("hideSplash");
|
||||
}
|
||||
}
|
||||
window.loadingView = new loadingView();
|
||||
if(window.loadingView)
|
||||
{
|
||||
window.loadingView.loadingAutoClose=true;//true代表当动画播放完毕,自动进入游戏。false为开发者手动控制
|
||||
window.loadingView.bgColor("#000000");//设置背景颜色
|
||||
window.loadingView.setFontColor("#ffffff");//设置字体颜色
|
||||
window.loadingView.setTips(["新世界的大门即将打开","敌军还有30秒抵达战场","妈妈说,心急吃不了热豆腐"]);//设置tips数组,会随机出现
|
||||
}
|
||||
window.onLayaInitError=function(e)
|
||||
{
|
||||
console.log("onLayaInitError error=" + e);
|
||||
alert("加载游戏失败,可能由于您的网络不稳定,请退出重进");
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
package layaair.autoupdateversion;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.webkit.ValueCallback;
|
||||
import layaair.autoupdateversion.data.VersionData;
|
||||
import layaair.game.config.config;
|
||||
|
||||
public class AutoUpdateAPK {
|
||||
//static public final String CHECK_VERSION_URL = "http://localhost:80/setuptest_update.xml";
|
||||
//static public final String APK_DOWNLOAD_PATH = "mnt/sdcard/";
|
||||
//static public final String DOWNLOAD_APK_NAME = "autoupdate.apk";
|
||||
|
||||
static private final int UPDATE_CHECKCOMPLETED = 1;
|
||||
static private final int UPDATE_DOWNLOADING = 2;
|
||||
static private final int UPDATE_DOWNLOAD_ERROR = 3;
|
||||
static private final int UPDATE_DOWNLOAD_COMPLETED = 4;
|
||||
static private final int UPDATE_DOWNLOAD_CANCELED = 5;
|
||||
|
||||
static private AutoUpdateAPK s_instance;
|
||||
|
||||
private layaair.autoupdateversion.IUpdateCallback m_callback;
|
||||
private String m_szDownloadAPKName;// = DOWNLOAD_APK_NAME;
|
||||
private String m_szDownloadPath;
|
||||
private int m_iVersionCode = 0;
|
||||
private int m_iProgressValue = 0;
|
||||
private Context m_context = null;
|
||||
private VersionData m_versionData = new VersionData();
|
||||
private boolean m_hasNewVersion = false;
|
||||
private boolean m_hasCanceled = false;
|
||||
private ValueCallback<Integer> m_pCallback;
|
||||
|
||||
public AutoUpdateAPK(Context p_context,ValueCallback<Integer> callback)
|
||||
{
|
||||
m_pCallback=callback;
|
||||
s_instance = this;
|
||||
m_callback = new UpdateCallback();
|
||||
m_context = p_context;
|
||||
this.m_szDownloadAPKName = config.GetInstance().getProperty("UpdateAPKFileName");
|
||||
this.m_szDownloadPath = config.GetInstance().getProperty("UpdateDownloadPath");
|
||||
setVersionCode(p_context);
|
||||
downloadVersionXML(config.GetInstance().getProperty("ApkUpdateUrl"));
|
||||
}
|
||||
public static void DelInstance()
|
||||
{
|
||||
s_instance=null;
|
||||
}
|
||||
|
||||
static public AutoUpdateAPK getInstance() {
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
public void cancelDownload() {
|
||||
m_hasCanceled = true;
|
||||
}
|
||||
|
||||
private static class UpdateHandle extends Handler
|
||||
{
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
AutoUpdateAPK that=AutoUpdateAPK.getInstance();
|
||||
switch (msg.what) {
|
||||
case UPDATE_CHECKCOMPLETED:
|
||||
that.m_callback.checkUpdateCompleted(that.getHasNewVersion(),
|
||||
that.m_versionData.getName() +that.m_versionData.getVersion());
|
||||
break;
|
||||
case UPDATE_DOWNLOADING:
|
||||
that.m_callback.downloadProgressChanged(that.m_iProgressValue);
|
||||
break;
|
||||
case UPDATE_DOWNLOAD_ERROR:
|
||||
onUpdateEnd(1);
|
||||
that.m_callback.downloadCompleted(false, msg.obj.toString());
|
||||
break;
|
||||
case UPDATE_DOWNLOAD_COMPLETED:
|
||||
onUpdateEnd(0);
|
||||
that.m_callback.downloadCompleted(true, "");
|
||||
break;
|
||||
case UPDATE_DOWNLOAD_CANCELED:
|
||||
that.m_callback.downloadCanceled();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void onUpdateEnd(int ecode)
|
||||
{
|
||||
if (ecode == 2 || ecode == 3) {
|
||||
s_instance.m_pCallback.onReceiveValue(1);
|
||||
} else {
|
||||
s_instance.m_pCallback.onReceiveValue(0);
|
||||
}
|
||||
}
|
||||
|
||||
Handler updateHandler = new UpdateHandle();
|
||||
|
||||
public void updateAPK() {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
File file = new File(m_szDownloadPath, m_szDownloadAPKName);
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
|
||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
Uri contentUri = android.support.v4.content.FileProvider.getUriForFile(m_context, "com.layabox.conch6.fileprovider", file);
|
||||
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
|
||||
} else {
|
||||
intent.setDataAndType(
|
||||
Uri.fromFile(file),
|
||||
"application/vnd.android.package-archive");
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
|
||||
m_context.startActivity(intent);
|
||||
}
|
||||
|
||||
public void downloadAPK() {
|
||||
new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
URL url = new URL(m_versionData.getDownloasURL());
|
||||
Log.i("","update apk url:"+url.toString());
|
||||
HttpURLConnection conn = (HttpURLConnection) url
|
||||
.openConnection();
|
||||
conn .setRequestProperty("Accept-Encoding", "identity");
|
||||
conn.setConnectTimeout(6 * 1000);
|
||||
conn.connect();
|
||||
// if (conn.getResponseCode() != 200)
|
||||
// throw new RuntimeException("请求url失败");
|
||||
|
||||
int length = conn.getContentLength();
|
||||
InputStream is = conn.getInputStream();
|
||||
|
||||
File fileAPK = new File(m_szDownloadPath,
|
||||
m_szDownloadAPKName);
|
||||
|
||||
if (fileAPK.exists()) {
|
||||
if(fileAPK.delete())
|
||||
{
|
||||
Log.i("","删除apk成功");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.e("","删除apk失败");
|
||||
}
|
||||
}
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(fileAPK);
|
||||
|
||||
int count = 0;
|
||||
byte buf[] = new byte[512];
|
||||
|
||||
int nLastProg=0;
|
||||
do {
|
||||
|
||||
int numread = is.read(buf);
|
||||
count += numread;
|
||||
m_iProgressValue = (int) (((float) count / length) * 100);
|
||||
if(m_iProgressValue!=nLastProg)
|
||||
{
|
||||
updateHandler.sendMessage(updateHandler.obtainMessage(UPDATE_DOWNLOADING));
|
||||
nLastProg = m_iProgressValue;
|
||||
}
|
||||
if (numread <= 0) {
|
||||
updateHandler.sendEmptyMessage(UPDATE_DOWNLOAD_COMPLETED);
|
||||
break;
|
||||
}
|
||||
fos.write(buf, 0, numread);
|
||||
} while (!m_hasCanceled);
|
||||
if (m_hasCanceled) {
|
||||
updateHandler.sendEmptyMessage(UPDATE_DOWNLOAD_CANCELED);
|
||||
}
|
||||
fos.close();
|
||||
is.close();
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
updateHandler.sendMessage(updateHandler.obtainMessage(UPDATE_DOWNLOAD_ERROR, e.getMessage()));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
updateHandler.sendMessage(updateHandler.obtainMessage(UPDATE_DOWNLOAD_ERROR, e.getMessage()));
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
public void setVersionCode(Context p_context) {
|
||||
try {
|
||||
m_iVersionCode = p_context.getPackageManager().getPackageInfo(
|
||||
p_context.getPackageName(), 0).versionCode;
|
||||
} catch (NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void downloadVersionXML(final String p_szURL) {
|
||||
this.setHasNewVersion(false);
|
||||
new Thread() {
|
||||
// ***************************************************************
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Random rnd;
|
||||
rnd = new Random();
|
||||
String url=p_szURL+"?r="+rnd.nextInt();
|
||||
Log.i("","update url="+url);
|
||||
String verjson = NetHelper
|
||||
.httpStringGet(url);
|
||||
parseVersionXMLFromString(verjson);
|
||||
if (m_versionData.getVersionCode() > m_iVersionCode) {
|
||||
setHasNewVersion(true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("","自动更新失败,应该是无法连接到"+p_szURL);
|
||||
}
|
||||
updateHandler.sendEmptyMessage(UPDATE_CHECKCOMPLETED);
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
public boolean parseVersionXMLFromString(String p_szContent) {
|
||||
DocumentBuilder db = null;
|
||||
Document document = null;
|
||||
try {
|
||||
db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
} catch (ParserConfigurationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
InputStream in = new ByteArrayInputStream(p_szContent.getBytes());
|
||||
assert db != null;
|
||||
document = db.parse(in);
|
||||
} catch (SAXException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Element root = null;
|
||||
if (document != null) {
|
||||
root = document.getDocumentElement();
|
||||
}
|
||||
// System.out.println("根节点名称:"+root.getTagName()); //根节点名称
|
||||
return doParseVersionXML(root);
|
||||
}
|
||||
|
||||
@SuppressLint({ "NewApi", "NewApi", "NewApi", "NewApi", "NewApi" })
|
||||
private boolean doParseVersionXML(Element p_root) {
|
||||
if(p_root==null)return false;
|
||||
NodeList __ls = p_root.getChildNodes();
|
||||
Node node;
|
||||
for (int i = 0; i < __ls.getLength(); i++) {
|
||||
node = __ls.item(i);
|
||||
if (node.getNodeName().toLowerCase(Locale.ENGLISH).equals("versioncode")) {
|
||||
m_versionData.setVersionCode(Integer.parseInt(node
|
||||
.getTextContent()));
|
||||
continue;
|
||||
}
|
||||
if (node.getNodeName().toLowerCase(Locale.ENGLISH).equals("version")) {
|
||||
m_versionData.setVersion(node.getTextContent());
|
||||
continue;
|
||||
}
|
||||
if (node.getNodeName().toLowerCase(Locale.ENGLISH).equals("name")) {
|
||||
m_versionData.setName(node.getTextContent());
|
||||
continue;
|
||||
}
|
||||
if (node.getNodeName().toLowerCase(Locale.ENGLISH).equals("url")) {
|
||||
m_versionData.setDownloasURL(node.getTextContent());
|
||||
}
|
||||
}
|
||||
//给apk地址加个参数,防止服务器的缓存
|
||||
m_versionData.setDownloasURL(m_versionData.getDownloasURL()+"?vc="+m_versionData.getVersionCode());
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean getHasNewVersion() {
|
||||
return m_hasNewVersion;
|
||||
}
|
||||
|
||||
public void setHasNewVersion(boolean m_hasNewVersion) {
|
||||
this.m_hasNewVersion = m_hasNewVersion;
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return this.m_context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package layaair.autoupdateversion;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AlertDialog.Builder;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
|
||||
public class DialogHelper {
|
||||
|
||||
public static void Confirm(Context ctx, CharSequence title, CharSequence message,
|
||||
CharSequence okText, OnClickListener oklistener, CharSequence cancelText,
|
||||
OnClickListener cancellistener) {
|
||||
AlertDialog.Builder builder = createDialog(ctx, title, message);
|
||||
builder.setPositiveButton(okText, oklistener);
|
||||
builder.setNegativeButton(cancelText, cancellistener);
|
||||
AlertDialog dlg = builder.create();
|
||||
dlg.setCanceledOnTouchOutside(false); //防止点到外面自动关掉对话框。
|
||||
dlg.show();
|
||||
}
|
||||
|
||||
private static AlertDialog.Builder createDialog(Context ctx, CharSequence title,
|
||||
CharSequence message) {
|
||||
AlertDialog.Builder builder = new Builder(ctx);
|
||||
builder.setMessage(message);
|
||||
if(title!=null)
|
||||
{
|
||||
builder.setTitle(title);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static AlertDialog.Builder createDialog(Context ctx,int titleId, int messageId) {
|
||||
AlertDialog.Builder builder = new Builder(ctx);
|
||||
builder.setMessage(messageId);
|
||||
builder.setTitle(titleId);
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package layaair.autoupdateversion;
|
||||
|
||||
public interface IUpdateCallback {
|
||||
void checkUpdateCompleted(Boolean hasUpdate,
|
||||
CharSequence updateInfo);
|
||||
|
||||
void downloadProgressChanged(int progress);
|
||||
void downloadCanceled();
|
||||
void downloadCompleted(Boolean sucess, CharSequence errorMsg);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package layaair.autoupdateversion;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.URL;
|
||||
|
||||
public class NetHelper {
|
||||
|
||||
public static String httpStringGet(String url) throws Exception {
|
||||
return httpStringGet(url, "utf-8");
|
||||
}
|
||||
|
||||
public static String httpStringGet(String url, String enc) throws Exception {
|
||||
HttpURLConnection connection = null;
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
URL u = new URL( url );
|
||||
connection = (HttpURLConnection) u.openConnection();
|
||||
connection.setRequestMethod( "GET" );
|
||||
connection.setConnectTimeout( 5000 );
|
||||
connection.setReadTimeout( 5000 );
|
||||
InputStream in = connection.getInputStream();
|
||||
reader = new BufferedReader( new InputStreamReader( in ) );
|
||||
StringBuilder result = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
result.append( line );
|
||||
}
|
||||
return String.valueOf( result );
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ProtocolException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package layaair.autoupdateversion;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.util.Log;
|
||||
|
||||
public class UpdateCallback implements IUpdateCallback {
|
||||
|
||||
static private final String DIALOG_DOWNLOAD_ERROR_TITLE = "下载失败";
|
||||
static private final String DIALOG_DOWNLOAD_ERROR_MSG = "下载更新文件失败";
|
||||
static private final String DIALOG_DOWNLOAD_BUTTON_TRY = "重试";
|
||||
static private final String DIALOG_DOWNLOAD_BUTTON_CANCEL= "取消";
|
||||
|
||||
static private final String DIALOG_UPDATE_TITLE = "更新";
|
||||
static private final String DIALOG_UPDATE_MSG = "立刻更新[";
|
||||
static private final String DIALOG_UPDATE_MSGEND = "]吗?";
|
||||
static private final String DIALOG_UPDATE_PROGRESS= "更新进度";
|
||||
static private final String DIALOG_UPDATE_BUTTON_TRY = "开始更新";
|
||||
static private final String DIALOG_UPDATE_BUTTON_CANCEL= "取消更新";
|
||||
|
||||
ProgressDialog updateProgressDialog = null;
|
||||
public void downloadProgressChanged(int progress) {
|
||||
if (updateProgressDialog != null
|
||||
&& updateProgressDialog.isShowing()) {
|
||||
updateProgressDialog.setProgress(progress);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void downloadCompleted(Boolean sucess, CharSequence errorMsg) {
|
||||
if (updateProgressDialog != null
|
||||
&& updateProgressDialog.isShowing()) {
|
||||
updateProgressDialog.dismiss();
|
||||
}
|
||||
if (sucess) {
|
||||
if (AutoUpdateAPK.getInstance() != null)
|
||||
AutoUpdateAPK.getInstance().updateAPK();
|
||||
} else {
|
||||
if (AutoUpdateAPK.getInstance() == null)
|
||||
return ;
|
||||
DialogHelper.Confirm(AutoUpdateAPK.getInstance().getContext(),
|
||||
DIALOG_DOWNLOAD_ERROR_TITLE,
|
||||
DIALOG_DOWNLOAD_ERROR_MSG,
|
||||
DIALOG_DOWNLOAD_BUTTON_TRY,
|
||||
new DialogInterface.OnClickListener() {
|
||||
|
||||
public void onClick(DialogInterface dialog,
|
||||
int which) {
|
||||
AutoUpdateAPK.getInstance().downloadAPK();
|
||||
|
||||
}
|
||||
}, DIALOG_DOWNLOAD_BUTTON_CANCEL, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void downloadCanceled() {
|
||||
Log.i("", "download canceled");
|
||||
}
|
||||
|
||||
public void checkUpdateCompleted(Boolean hasUpdate, CharSequence updateInfo) {
|
||||
if (AutoUpdateAPK.getInstance() == null)
|
||||
return ;
|
||||
if (hasUpdate)
|
||||
{
|
||||
DialogHelper.Confirm(
|
||||
AutoUpdateAPK.getInstance().getContext(),
|
||||
DIALOG_UPDATE_TITLE,
|
||||
DIALOG_UPDATE_MSG + updateInfo+ DIALOG_UPDATE_MSGEND,DIALOG_UPDATE_BUTTON_TRY,
|
||||
new DialogInterface.OnClickListener() {
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
updateProgressDialog = new ProgressDialog(AutoUpdateAPK.getInstance().getContext());
|
||||
updateProgressDialog.setMessage(DIALOG_UPDATE_PROGRESS);
|
||||
updateProgressDialog.setIndeterminate(false);
|
||||
updateProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
updateProgressDialog.setMax(100);
|
||||
updateProgressDialog.setProgress(0);
|
||||
updateProgressDialog.setCancelable(false);
|
||||
updateProgressDialog.setCanceledOnTouchOutside(false);
|
||||
updateProgressDialog.show();
|
||||
|
||||
AutoUpdateAPK.getInstance().downloadAPK();
|
||||
}
|
||||
}, DIALOG_UPDATE_BUTTON_CANCEL,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
AutoUpdateAPK.onUpdateEnd(3);
|
||||
}
|
||||
}
|
||||
);
|
||||
}else{
|
||||
AutoUpdateAPK.onUpdateEnd(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package layaair.autoupdateversion.data;
|
||||
|
||||
public class VersionData {
|
||||
private int m_iVersionCode;
|
||||
private String m_szName;
|
||||
private String m_szVersion;
|
||||
private String m_szDownloasURL;
|
||||
|
||||
public int getVersionCode() {
|
||||
return m_iVersionCode;
|
||||
}
|
||||
|
||||
public void setVersionCode(int p_iVersionCode) {
|
||||
this.m_iVersionCode = p_iVersionCode;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return m_szName;
|
||||
}
|
||||
|
||||
public void setName(String p_szName) {
|
||||
this.m_szName = p_szName;
|
||||
}
|
||||
|
||||
public String getDownloasURL() {
|
||||
return m_szDownloasURL;
|
||||
}
|
||||
|
||||
public void setDownloasURL(String p_szDownloasURL) {
|
||||
this.m_szDownloasURL = p_szDownloasURL;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return m_szVersion;
|
||||
}
|
||||
|
||||
public void setVersion(String m_szVersion) {
|
||||
this.m_szVersion = m_szVersion;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package layaair.game.browser;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.google.zxing.integration.android.IntentIntegrator;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import layaair.game.config.config;
|
||||
|
||||
import static android.content.Context.CONNECTIVITY_SERVICE;
|
||||
|
||||
|
||||
public class JSBridge {
|
||||
public static Handler m_Handler = new Handler(Looper.getMainLooper());
|
||||
public static Activity mMainActivity = null;
|
||||
|
||||
public static void hideSplash() {
|
||||
m_Handler.post(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
MainActivity.mSplashDialog.dismissSplash();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void setFontColor(final String color) {
|
||||
m_Handler.post(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
MainActivity.mSplashDialog.setFontColor(Color.parseColor(color));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void setTips(final JSONArray tips) {
|
||||
m_Handler.post(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
String[] tipsArray = new String[tips.length()];
|
||||
for (int i = 0; i < tips.length(); i++) {
|
||||
tipsArray[i] = tips.getString(i);
|
||||
}
|
||||
MainActivity.mSplashDialog.setTips(tipsArray);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void bgColor(final String color) {
|
||||
m_Handler.post(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
MainActivity.mSplashDialog.setBackgroundColor(Color.parseColor(color));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void loading(final double percent) {
|
||||
m_Handler.post(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
MainActivity.mSplashDialog.setPercent((int)percent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void showTextInfo(final boolean show) {
|
||||
m_Handler.post(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
MainActivity.mSplashDialog.showTextInfo(show);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void showFloatPanel(final boolean show) {
|
||||
m_Handler.post(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
if (MainActivity.m_FloatPanel != null) {
|
||||
if (show) {
|
||||
MainActivity.m_FloatPanel.show();
|
||||
} else {
|
||||
MainActivity.m_FloatPanel.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void showScanner(final boolean show) {
|
||||
m_Handler.post(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
if (MainActivity.m_FloatPanel != null) {
|
||||
if (show) {
|
||||
new IntentIntegrator(mMainActivity).setCaptureActivity(ScanActivity.class).initiateScan();
|
||||
} else {
|
||||
if (ScanActivity.m_instance != null) {
|
||||
ScanActivity.m_instance.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void getIP() {
|
||||
m_Handler.post(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
ExportJavaFunction.CallBackToJS(JSBridge.class,"getIP", getIP(mMainActivity));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void onOrientationChange() {
|
||||
MainActivity.m_FloatPanel.updatePosition();
|
||||
}
|
||||
|
||||
public static void onScanResult(String result) {
|
||||
Log.d("JSBridge", "url " + result);
|
||||
if (ScanActivity.m_instance != null) {
|
||||
ScanActivity.m_instance.finish();
|
||||
}
|
||||
String js = "script.UIController.instance.onScanResult('";
|
||||
js += result;
|
||||
js += "');";
|
||||
ConchJNI.RunJS(js);
|
||||
}
|
||||
public static String getIP(Context context){
|
||||
try {
|
||||
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
|
||||
NetworkInterface intf = en.nextElement();
|
||||
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
|
||||
InetAddress inetAddress = enumIpAddr.nextElement();
|
||||
if (!inetAddress.isLoopbackAddress() && (inetAddress instanceof Inet4Address)) {
|
||||
NetworkInfo activeNetInfo = ((ConnectivityManager)context.getSystemService(CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
|
||||
String str = "";
|
||||
if(activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI) {
|
||||
str += "WifiNetworkIP: ";
|
||||
}
|
||||
else if(activeNetInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
|
||||
str += "MobileNetworkIP: ";
|
||||
}
|
||||
else {
|
||||
str += "UnknowNetworkIP: ";
|
||||
}
|
||||
str += inetAddress.getHostAddress().toString();
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SocketException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,644 @@
|
||||
package layaair.game.browser;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.os.Build;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.ValueCallback;
|
||||
import android.widget.AbsoluteLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import layaair.autoupdateversion.AutoUpdateAPK;
|
||||
import layaair.game.conch.ILayaEventListener;
|
||||
import layaair.game.conch.LayaConch5;
|
||||
import layaair.game.config.config;
|
||||
import layaair.game.wrapper.ILayaLibWrapper;
|
||||
|
||||
@SuppressLint("Wakelock")
|
||||
public class LayaWrapper implements ILayaLibWrapper{
|
||||
public static final int AR_CHECK_UPDATE = 1;
|
||||
public static final int AR_INIT_PLATFORM = 2;
|
||||
private static int m_nStartActivityType = 0; //设置网络之后,再次调用到哪个函数的标记
|
||||
|
||||
public static final int BACK_TO_MAIN = 0;
|
||||
public static final int REFRESH = 1;
|
||||
public static final int CLOSE_BIG = 2;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static LayaWrapper ms_layaEngine = null;
|
||||
public boolean m_bPopAD = true;
|
||||
static public AbsoluteLayout m_pAbsEditLayout = null;
|
||||
private long m_nBackPressTime=0;
|
||||
|
||||
//kuo change code here
|
||||
public View m_pExternalLoadingView = null;
|
||||
static private Toast mToast;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
public SensorManager m_pSensorManager = null;
|
||||
public Sensor m_pSensor = null;
|
||||
public SensorEventListener m_pSensorListener = null;
|
||||
public int SENSOR_OFFSET = 6;
|
||||
public double m_fSensorX = 0;
|
||||
public double m_fSensorY = 0;
|
||||
public double m_fSensorZ = 0;
|
||||
public int m_nScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
|
||||
private String tempSoPath = "";
|
||||
private String tempSoFile = "";
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
public static Activity m_LayaEngineContext = null;
|
||||
public static LayaConch5 m_pEngine = null;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
public static LayaWrapper GetInstance()
|
||||
{
|
||||
if (ms_layaEngine == null) {
|
||||
ms_layaEngine = new LayaWrapper();
|
||||
}
|
||||
return ms_layaEngine;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
public LayaWrapper()
|
||||
{
|
||||
|
||||
}
|
||||
public void setLayaEventListener(ILayaEventListener _lis){
|
||||
if(m_pEngine!=null)
|
||||
m_pEngine.setLayaEventListener(_lis);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
public void initEngine(Activity pContext)
|
||||
{
|
||||
ms_layaMainActivity = pContext;
|
||||
ms_layaEngine = this;
|
||||
m_LayaEngineContext = pContext;
|
||||
ms_mCtx = m_LayaEngineContext;//.getApplicationContext();
|
||||
/*try{
|
||||
InputStream is = m_LayaEngineContext.getResources().getAssets().open("config.ini");
|
||||
config.GetInstance().init( is );
|
||||
}catch(Exception e){
|
||||
Log.e("","打开配置文件错误。");
|
||||
}*/
|
||||
m_pEngine = new LayaConch5(ms_mCtx);
|
||||
m_pEngine.game_conch3_SetIsPlug(false);
|
||||
if (tempSoPath.length()>0) {
|
||||
m_pEngine.setSoPath(tempSoPath);
|
||||
}
|
||||
if (tempSoFile.length()>0) {
|
||||
m_pEngine.setSoPath(tempSoFile);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
public void setSoPath(String pSoPath)
|
||||
{
|
||||
tempSoPath = pSoPath;
|
||||
if (m_pEngine!=null) {
|
||||
m_pEngine.setSoPath(pSoPath);
|
||||
}
|
||||
}
|
||||
public void setAlertTitle(String title) {
|
||||
if (m_pEngine!=null) {
|
||||
m_pEngine.setAlertTitle(title);
|
||||
}
|
||||
}
|
||||
public void setStringOnBackPressed(String str) {
|
||||
if (m_pEngine!=null) {
|
||||
m_pEngine.setStringOnBackPressed(str);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
public void setSoFile(String pSoFile)
|
||||
{
|
||||
tempSoFile = pSoFile;
|
||||
if (m_pEngine!=null) {
|
||||
m_pEngine.setSoFile(pSoFile);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
public void setGameUrl(String pUrl)
|
||||
{
|
||||
m_pEngine.setGameUrl(pUrl);
|
||||
}
|
||||
|
||||
|
||||
public void setLocalizable(boolean b)
|
||||
{
|
||||
m_pEngine.setLocalizable(b);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
public void startGame()
|
||||
{
|
||||
EngineStart();
|
||||
}
|
||||
public String getCacheDir() {
|
||||
//return m_LayaEngineContext.getCacheDir().toString();
|
||||
String sCache = m_LayaEngineContext.getCacheDir().toString();
|
||||
String []vString = sCache.split("/");
|
||||
String sNewCache="";
|
||||
for( int i = 0; i < vString.length-1; i++ )
|
||||
{
|
||||
sNewCache += vString[i];
|
||||
sNewCache += "/";
|
||||
}
|
||||
return sNewCache;
|
||||
}
|
||||
// ------------------------------------------------------------------------------
|
||||
public void EngineStart()
|
||||
{
|
||||
String cachePath = getCacheDir();
|
||||
String strLayaCache = cachePath + "/LayaCache";
|
||||
File cacheFolder=new File(strLayaCache);
|
||||
if(!cacheFolder.exists()) {
|
||||
cacheFolder.mkdir();
|
||||
}
|
||||
AssetManager am = LayaWrapper.getLayaApplicationAsset();
|
||||
/*if (m_pExternalLoadingView!=null) {
|
||||
m_pEngine.setLoadingView(m_pExternalLoadingView);
|
||||
}else {
|
||||
m_pEngine.setLoadingView(m_pLoadingView);
|
||||
}*/
|
||||
m_pEngine.game_conch3_setAppWorkPath(cachePath);
|
||||
m_pEngine.game_conch3_setAssetInfo(am);
|
||||
m_pEngine.game_conch3_init();
|
||||
//准备localStorage目录
|
||||
File localStoragePath = new File(strLayaCache + "/localstorage");
|
||||
if (!localStoragePath.exists()) {
|
||||
if (!localStoragePath.mkdirs()) {
|
||||
Log.e("", "创建localstorage目录失败!");
|
||||
}
|
||||
}
|
||||
ConchJNI.SetLocalStoragePath(strLayaCache + "/localstorage");
|
||||
|
||||
//环境都准备好了,开始流程了。
|
||||
if (PermisionUtils.checkExternalStoragePermission(ms_layaMainActivity)) {
|
||||
checkApkUpdate();
|
||||
}
|
||||
}
|
||||
public boolean isOpenNetwork(Context context)
|
||||
{
|
||||
if (!config.GetInstance().m_bCheckNetwork)
|
||||
return true;
|
||||
ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
return connManager.getActiveNetworkInfo() != null && (connManager.getActiveNetworkInfo().isAvailable() && connManager.getActiveNetworkInfo().isConnected());
|
||||
}
|
||||
public void settingNetwork(final Context context, final int p_nType)
|
||||
{
|
||||
AlertDialog.Builder pBuilder = new AlertDialog.Builder(context);
|
||||
pBuilder.setTitle("连接失败,请检查网络或与开发商联系").setMessage("是否对网络进行设置?");
|
||||
// 退出按钮
|
||||
pBuilder.setPositiveButton("是", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface p_pDialog, int arg1) {
|
||||
Intent intent;
|
||||
try {
|
||||
String sdkVersion = android.os.Build.VERSION.SDK;
|
||||
if (Integer.valueOf(sdkVersion) > 10) {
|
||||
intent = new Intent(
|
||||
android.provider.Settings.ACTION_WIRELESS_SETTINGS);
|
||||
} else {
|
||||
intent = new Intent();
|
||||
ComponentName comp = new ComponentName(
|
||||
"com.android.settings",
|
||||
"com.android.settings.WirelessSettings");
|
||||
intent.setComponent(comp);
|
||||
intent.setAction("android.intent.action.VIEW");
|
||||
}
|
||||
((Activity)context).startActivityForResult(intent, p_nType);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
pBuilder.setNegativeButton("否", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.cancel();
|
||||
((Activity)context).finish();
|
||||
}
|
||||
});
|
||||
AlertDialog alertdlg = pBuilder.create();
|
||||
alertdlg.setCanceledOnTouchOutside(false);
|
||||
alertdlg.show();
|
||||
}
|
||||
public void checkApkUpdate( Context context,final ValueCallback<Integer> callback)
|
||||
{
|
||||
if (isOpenNetwork(context)) {
|
||||
// 自动版本更新
|
||||
if ( "0".equals(config.GetInstance().getProperty("IsHandleUpdateAPK","0")) == false && ContextCompat.checkSelfPermission(ms_layaMainActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
|
||||
Log.e("0", "==============Java流程 checkApkUpdate");
|
||||
new AutoUpdateAPK(context, new ValueCallback<Integer>() {
|
||||
@Override
|
||||
public void onReceiveValue(Integer integer) {
|
||||
Log.e("",">>>>>>>>>>>>>>>>>>");
|
||||
callback.onReceiveValue(integer);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Log.e("0", "==============Java流程 checkApkUpdate 不需要自己管理update");
|
||||
callback.onReceiveValue(1);
|
||||
}
|
||||
} else {
|
||||
settingNetwork(context,1);
|
||||
}
|
||||
}
|
||||
public void checkApkUpdate() {
|
||||
checkApkUpdate(m_LayaEngineContext,new ValueCallback<Integer>() {
|
||||
@Override
|
||||
public void onReceiveValue(Integer integer) {
|
||||
if (integer.intValue() == 1) {
|
||||
InitView();
|
||||
} else {
|
||||
((Activity)m_LayaEngineContext).finish();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public void InitView() {
|
||||
View view = m_pEngine.game_conch3_get_view();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
|
||||
}
|
||||
m_LayaEngineContext.setContentView(view);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
//获取当前应用的版本名称
|
||||
static public String getAppVersionName()
|
||||
{
|
||||
String versionName = "";
|
||||
int versioncode = 0;
|
||||
try {
|
||||
// ---get the package info---
|
||||
PackageManager pm = LayaWrapper.ms_mCtx.getPackageManager();
|
||||
PackageInfo pi = pm.getPackageInfo(LayaWrapper.ms_mCtx.getPackageName(), 0);
|
||||
versionName = pi.versionName;
|
||||
versioncode = pi.versionCode;
|
||||
if (versionName == null || versionName.length() <= 0) {
|
||||
return "";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("VersionInfo", "Exception", e);
|
||||
}
|
||||
return versionName;
|
||||
}
|
||||
/*
|
||||
* 从assets目录下拷贝文件 srcFile 只能是资源根目录下的文件
|
||||
*/
|
||||
public void CopyFileFromAssets(String srcFile, String destPath) {
|
||||
File curWorkingPath = new File(destPath);
|
||||
if (!curWorkingPath.exists()) {
|
||||
Log.i("", "mkdir:" + destPath);
|
||||
if (!curWorkingPath.mkdirs()) {
|
||||
// TODO 要验证,看文件夹和文件的处理方式
|
||||
Log.e("", "copyasserts error: 创建文件夹出错,dir=" + destPath);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
File outFile = new File(curWorkingPath, srcFile);
|
||||
InputStream in = null;
|
||||
|
||||
try {
|
||||
in = m_LayaEngineContext.getAssets().open(srcFile);
|
||||
Log.i("", "copy file: " + srcFile);
|
||||
} catch (IOException e) {
|
||||
Log.e("", "open file err:" + srcFile);
|
||||
return;
|
||||
}
|
||||
|
||||
OutputStream out = new FileOutputStream(outFile);
|
||||
// Transfer bytes from in to out
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
in.close();
|
||||
out.close();
|
||||
Log.e("", "拷贝文件" + srcFile + "成功");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
public void InitSensor() {
|
||||
m_pSensorManager = (SensorManager) m_LayaEngineContext.getSystemService(Context.SENSOR_SERVICE);
|
||||
m_pSensor = m_pSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||
m_pSensorListener = new SensorEventListener() {
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
}
|
||||
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
double fX = event.values[SensorManager.DATA_X];
|
||||
double fY = event.values[SensorManager.DATA_Y];
|
||||
double fZ = event.values[SensorManager.DATA_Z];
|
||||
if (Math.abs(m_fSensorX - fX) > 0.1
|
||||
|| Math.abs(m_fSensorY - fY) > 0.1
|
||||
|| Math.abs(m_fSensorZ - fZ) > 0.1) {
|
||||
m_fSensorX = fX;
|
||||
m_fSensorY = fY;
|
||||
m_fSensorZ = fZ;
|
||||
if (fX >= (SENSOR_OFFSET - 1) && fX <= (SENSOR_OFFSET + 1)
|
||||
&& Math.abs(fY) < 2) {
|
||||
ConchJNI.onSensorChanged(-1);
|
||||
// Log.e("0", ">>>>>>>arc=-1" + ",x=" + fX + ",y=" + fY
|
||||
// + ",z=" + fZ );
|
||||
} else {
|
||||
double fArc = Math.atan2(fY, fX
|
||||
- SENSOR_OFFSET)
|
||||
- Math.PI / 2.0f;
|
||||
fArc = (fArc < 0) ? (fArc + Math.PI * 2) : fArc;
|
||||
ConchJNI.onSensorChanged((float) (fArc));
|
||||
// Log.e("0", ">>>>>>>arc=" + fArc + ",x=" + fX + ",y="
|
||||
// + fY + ",z=" + fZ );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
m_pSensorManager.registerListener(m_pSensorListener, m_pSensor,
|
||||
SensorManager.SENSOR_DELAY_GAME);
|
||||
}
|
||||
|
||||
public void handleUncaughtException (Thread thread, Throwable e){
|
||||
e.printStackTrace(); // not all Android versions will print the stack trace automatically
|
||||
Intent intent = new Intent ();
|
||||
intent.setAction ("com.dawawa.SEND_LOG");
|
||||
intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK); // required when starting from Application
|
||||
m_LayaEngineContext.startActivity (intent);
|
||||
System.exit(1); // kill off the crashed app
|
||||
}
|
||||
// ------------------------------------------------------------------------------
|
||||
public static void onPopMenu(int id)
|
||||
{
|
||||
switch (id) {
|
||||
case BACK_TO_MAIN:
|
||||
//Toast.makeText(GetInstance(), "BACK_TO_MAIN", Toast.LENGTH_SHORT).show();
|
||||
ConchJNI.onRunCmd(0x400 + 3333 + 104, -1, 0);
|
||||
break;
|
||||
case CLOSE_BIG:
|
||||
//Toast.makeText(GetInstance(), "CLOSE_BIG", Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case REFRESH:
|
||||
//Toast.makeText(GetInstance(), "REFRESH", Toast.LENGTH_SHORT).show();
|
||||
ConchJNI.onRunCmd(0x400 + 3333 + 102, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------------------
|
||||
// public void initPlatform() {
|
||||
// // 初始化平台c
|
||||
// m_pEngine.setMarketName( config.GetInstance().m_sMarketPlatformClassname!=null?config.GetInstance().m_sMarketPlatformClassname:"");
|
||||
// if ((config.GetInstance().m_sMarketPlatformClassname != null)
|
||||
// && (config.GetInstance().m_sMarketPlatformClassname.length() > 1)) {
|
||||
// if (isOpenNetwork()) {
|
||||
// Log.e("0", "==============Java流程 第三方平台初始化");
|
||||
// m_pPlatform.LP_Init(m_LayaEngineContext);
|
||||
// } else {
|
||||
// settingNetwork(AR_INIT_PLATFORM);
|
||||
// }
|
||||
// } else {
|
||||
// Log.e("0", "==============Java流程 没有第三方平台直接调用 PlatformInitOK");
|
||||
// PlatformInitOK(0);
|
||||
// }
|
||||
// }
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
public void PlatformInitOK(int p_nFlag) {
|
||||
Log.e("0", "==============Java流程 InitMainCanvas()");
|
||||
InitView();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
public void onActivityResult(int requestCode, int resultCode,Intent intent) {
|
||||
if (requestCode == AR_INIT_PLATFORM) {
|
||||
InitView();
|
||||
} else if (requestCode == AR_CHECK_UPDATE) {
|
||||
checkApkUpdate();
|
||||
}
|
||||
m_pEngine.onActivityResult(requestCode,resultCode,intent);
|
||||
/*
|
||||
* if( m_pPlatform != null ) { m_pPlatform.LP_handleActivityResult(
|
||||
* requestCode, resultCode, intent); }
|
||||
*/
|
||||
}
|
||||
|
||||
public void setResolution(int w, int h){
|
||||
m_pEngine.setResolution(w,h);
|
||||
}
|
||||
|
||||
public void _enableOnLayout(boolean b){
|
||||
m_pEngine._enableOnLayout(b);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
public void onPause() {
|
||||
if (m_pSensorManager != null) {
|
||||
m_pSensorManager.unregisterListener(m_pSensorListener);
|
||||
}
|
||||
m_pEngine.game_conch3_onPause();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
public void onResume() {
|
||||
|
||||
if( m_pSensorManager != null ) {
|
||||
m_pSensorManager.registerListener( m_pSensorListener, m_pSensor, SensorManager.SENSOR_DELAY_GAME );
|
||||
}
|
||||
m_pEngine.game_conch3_onResume();
|
||||
}
|
||||
|
||||
public void onDestroy(){
|
||||
m_pEngine.onDestroy();
|
||||
DelInstance();
|
||||
}
|
||||
|
||||
private static void DelInstance()
|
||||
{
|
||||
AutoUpdateAPK.DelInstance();
|
||||
m_pEngine=null;
|
||||
m_LayaEngineContext=null;
|
||||
ms_layaEngine=null;
|
||||
ms_layaMainActivity=null;
|
||||
ms_mCtx=null;
|
||||
}
|
||||
@Override
|
||||
public void onStop() {
|
||||
m_pEngine.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
m_pEngine.onNewIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestart() {
|
||||
m_pEngine.onRestart();
|
||||
}
|
||||
|
||||
public void setInterceptKey(boolean _intercept)
|
||||
{
|
||||
m_pEngine.setInterceptKey(_intercept);
|
||||
}
|
||||
public void setOptions(HashMap<String, Object> _option)
|
||||
{
|
||||
for (Object o : _option.entrySet()) {
|
||||
Entry entry = (Entry) o;
|
||||
String key = (String) entry.getKey();
|
||||
Log.i("setOption", "setOptions() key=" + key + " value=" + entry.getValue());
|
||||
if (key.compareToIgnoreCase("url") == 0) {
|
||||
m_pEngine.setGameUrl((String) entry.getValue());
|
||||
} else if (key.compareToIgnoreCase("sopath") == 0) {
|
||||
setSoPath((String) entry.getValue());
|
||||
} else if (key.compareToIgnoreCase("sofile") == 0) {
|
||||
setSoFile((String) entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean IsFinishing(Activity context)
|
||||
{
|
||||
if(context==null||context.isFinishing())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
// ------------------------------------------------------------------------------
|
||||
public static void ShowMessage(String pMessage, LayaConch5 _pEngine){
|
||||
_pEngine.showMessage(pMessage);
|
||||
}
|
||||
// ------------------------------------------------------------------------------
|
||||
static public int GetScreenWidth() {
|
||||
DisplayMetrics pDm = new DisplayMetrics();
|
||||
LayaWrapper.getLayaApplicationActivity().getWindowManager().getDefaultDisplay().getMetrics( pDm );
|
||||
return pDm.widthPixels;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
static public int GetScreenHeight() {
|
||||
DisplayMetrics pDm = new DisplayMetrics();
|
||||
LayaWrapper.getLayaApplicationActivity().getWindowManager().getDefaultDisplay().getMetrics( pDm );
|
||||
return pDm.heightPixels;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
static public float GetScreenInch() {
|
||||
DisplayMetrics pDm = new DisplayMetrics();
|
||||
LayaWrapper.getLayaApplicationActivity().getWindowManager().getDefaultDisplay().getMetrics( pDm );
|
||||
int nWidth = pDm.widthPixels;
|
||||
int nHeight = pDm.heightPixels;
|
||||
float nDpi = pDm.densityDpi;
|
||||
return (float) (Math.sqrt(nWidth * nWidth + nHeight * nHeight) / nDpi);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
public Bitmap getResImage(String name) {
|
||||
ApplicationInfo pAppInfo = m_LayaEngineContext.getApplicationInfo();
|
||||
int nResID = m_LayaEngineContext.getResources().getIdentifier(name, "drawable",pAppInfo.packageName);
|
||||
return BitmapFactory.decodeResource(m_LayaEngineContext.getResources(), nResID);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
static public void reloadApp(){
|
||||
ConchJNI.reloadJS();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// 通过 AlarmManager 来重新启动应用
|
||||
public void restartApp() {
|
||||
Intent intent = new Intent(m_LayaEngineContext.getApplicationContext(), LayaWrapper.class);
|
||||
PendingIntent restartIntent = PendingIntent.getActivity(
|
||||
m_LayaEngineContext.getApplicationContext(), 0, intent,Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
// 退出程序
|
||||
AlarmManager mgr = (AlarmManager) m_LayaEngineContext.getSystemService(Context.ALARM_SERVICE);
|
||||
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000,
|
||||
restartIntent); // 1秒钟后重启应用
|
||||
m_LayaEngineContext.finishActivity(0);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
static public void MyJSAlert(String title, String msg, int p_nCallbackType, final LayaConch5 _gameEngine) {
|
||||
if(_gameEngine!=null)
|
||||
_gameEngine.alertJS(title,msg,p_nCallbackType);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
static public void setScreenWakeLock( boolean p_bWakeLock ) {
|
||||
Activity activity = LayaWrapper.getLayaApplicationActivity();
|
||||
if(activity==null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if( p_bWakeLock )
|
||||
{
|
||||
LayaWrapper.getLayaApplicationActivity().getWindow().addFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON );
|
||||
}
|
||||
else
|
||||
{
|
||||
LayaWrapper.getLayaApplicationActivity().getWindow().clearFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON );
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------------------
|
||||
/*
|
||||
* The follow code for plugin implementation
|
||||
* author:kuo
|
||||
*{{ begin
|
||||
*/
|
||||
static private Activity ms_layaMainActivity=null;
|
||||
static public Context ms_mCtx=null;
|
||||
|
||||
static public Context getLayaApplicationContext()
|
||||
{
|
||||
return ms_mCtx;
|
||||
}
|
||||
|
||||
static public Activity getLayaApplicationActivity()
|
||||
{
|
||||
return ms_layaMainActivity;
|
||||
}
|
||||
|
||||
static public AssetManager getLayaApplicationAsset()
|
||||
{
|
||||
return ms_layaMainActivity.getAssets();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setLoadingView(View view) {
|
||||
m_pExternalLoadingView = view;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
package layaair.game.browser;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AlertDialog.Builder;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import com.google.zxing.integration.android.IntentIntegrator;
|
||||
import com.google.zxing.integration.android.IntentResult;
|
||||
import layaair.game.conch.ILayaEventListener;
|
||||
import layaair.game.conch.LayaConch5;
|
||||
import layaair.game.floatmenu.FloatPanel;
|
||||
import layaair.game.wrapper.ILayaLibWrapper;
|
||||
import layaair.game.wrapper.LayaWrapperFactroy;
|
||||
import com.layabox.conch6.R;
|
||||
|
||||
@SuppressLint("Wakelock")
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
//------------------------------------------------------------------------------
|
||||
private long m_nBackPressTime=0;
|
||||
public ILayaLibWrapper mLayaEngine = null;
|
||||
public static SplashDialog mSplashDialog;
|
||||
public static FloatPanel m_FloatPanel = null;;
|
||||
// ------------------------------------------------------------------------------
|
||||
protected void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
JSBridge.mMainActivity = this;
|
||||
mSplashDialog = new SplashDialog(this);
|
||||
mSplashDialog.showSplash();
|
||||
|
||||
m_FloatPanel = new FloatPanel(this);
|
||||
|
||||
setFullScreen(true);
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(LayaConch5.MARKET_MARKETNAME, "LayaMarket");
|
||||
LayaConch5.setMarketBundle(bundle);
|
||||
|
||||
mLayaEngine = LayaWrapperFactroy.createLayaWrapper();
|
||||
mLayaEngine.initEngine(this);
|
||||
mLayaEngine.setAlertTitle(this.getString( R.string.alert_dialog_title ));
|
||||
mLayaEngine.setStringOnBackPressed(this.getString( R.string.on_back_pressed ));
|
||||
|
||||
layaGameListener listener=new layaGameListener();
|
||||
listener.activity=this;
|
||||
mLayaEngine.setLayaEventListener(listener);
|
||||
mLayaEngine.setInterceptKey(true);
|
||||
Intent intent = getIntent();
|
||||
String scheme = intent.getScheme();
|
||||
Uri uri = intent.getData();
|
||||
Log.e("2jni", "scheme:" + scheme);
|
||||
mLayaEngine.setLocalizable(false);
|
||||
if (uri != null) {
|
||||
String host = uri.getHost();
|
||||
String dataString = intent.getDataString();
|
||||
String id = uri.getQueryParameter("d");
|
||||
String path = uri.getPath();
|
||||
String path1 = uri.getEncodedPath();
|
||||
String queryString = uri.getQuery();
|
||||
Log.e("2jni", "host:" + host);
|
||||
Log.e("2jni", "dataString:" + dataString);
|
||||
Log.e("2jni", "id:" + id);
|
||||
Log.e("2jni", "path:" + path);
|
||||
Log.e("2jni", "path1:" + path1);
|
||||
Log.e("2jni", "queryString:" + queryString);
|
||||
if (dataString.length() > 0) {
|
||||
String gameUrl = "http" + dataString.substring(7);
|
||||
Log.e("2jni", "setUrl from intent:" + gameUrl);
|
||||
mLayaEngine.setGameUrl(gameUrl);
|
||||
}
|
||||
}
|
||||
/*else
|
||||
{
|
||||
mLayaEngine.setGameUrl("http://10.10.30.29:8899/conch-main.html");
|
||||
}*/
|
||||
// HashMap<String, Object> _option = new HashMap<String, Object>();
|
||||
// _option.put("sopath", "/data/data/com.layabox.conch3/pluginslib/");
|
||||
// mLayaEngine.setOptions(_option);
|
||||
// mLayaEngine.setGameUrl("http://10.10.30.29:8888/starthtml/index4.html");
|
||||
// mLayaEngine.setGameUrl("http://192.168.0.106:8888/starthtml/default1.html");
|
||||
|
||||
mLayaEngine.startGame();
|
||||
//删除全部推送消息
|
||||
//LayaNotifyManager.removeAllNotify();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
mLayaEngine.onStop();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
|
||||
if(result != null) {
|
||||
if(result.getContents() == null) {
|
||||
Log.d("MainActivity", "Cancelled scan");
|
||||
} else {
|
||||
Log.d("MainActivity", "Scanned");
|
||||
JSBridge.onScanResult(result.getContents());
|
||||
}
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
((LayaWrapper)mLayaEngine).onActivityResult(requestCode, resultCode, intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestart() {
|
||||
super.onRestart();
|
||||
mLayaEngine.onRestart();
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
public void setFullScreen(boolean p_bFull) {
|
||||
if (p_bFull) {
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
} else {
|
||||
requestWindowFeature(Window.FEATURE_CONTEXT_MENU);
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------------------
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mLayaEngine.onPause();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mLayaEngine.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy(){
|
||||
super.onDestroy();
|
||||
mLayaEngine.onDestroy();
|
||||
mLayaEngine=null;
|
||||
m_FloatPanel.destory();
|
||||
m_FloatPanel = null;
|
||||
mSplashDialog.dismiss();
|
||||
mSplashDialog = null;
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
mLayaEngine.onNewIntent(intent);
|
||||
}
|
||||
// ------------------------------------------------------------------------------
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// menu.add(0, 0, 0, "refresh");
|
||||
menu.add(0, 1, 1, "version");
|
||||
menu.add(Menu.NONE, 2, 2, "url");
|
||||
menu.add(Menu.NONE, 3, 3, "deviceInfo");
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
// ------------------------------------------------------------------------------
|
||||
public void MyAlert(String title, String msg) {
|
||||
Builder pBuilder = new Builder(this);
|
||||
pBuilder.setTitle(title);
|
||||
pBuilder.setMessage(msg);
|
||||
// 退出按钮
|
||||
pBuilder.setPositiveButton("OK", new OnClickListener() {
|
||||
public void onClick(DialogInterface p_pDialog, int arg1) {
|
||||
p_pDialog.cancel();
|
||||
}
|
||||
});
|
||||
AlertDialog alertdlg = pBuilder.create();
|
||||
alertdlg.setCanceledOnTouchOutside(false);
|
||||
alertdlg.show();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
public boolean onOptionsItemSelected(MenuItem menuItem) {
|
||||
super.onOptionsItemSelected(menuItem);
|
||||
switch (menuItem.getItemId()) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
MyAlert("", LayaWrapper.getAppVersionName());
|
||||
break;
|
||||
case 2:
|
||||
MyAlert("", ExportJavaFunction.m_sHref);
|
||||
break;
|
||||
case 3: {
|
||||
// 测试用的
|
||||
int nW = LayaWrapper.GetScreenWidth();
|
||||
int nH = LayaWrapper.GetScreenHeight();
|
||||
int nDpi =1;
|
||||
float nInch = LayaWrapper.GetScreenInch();
|
||||
int nMoveRange = LayaWrapper.m_pEngine.game_plugin_getTouchMovRange();
|
||||
MyAlert("", "w=" + nW + ",h=" + nH + ",dpi=" + nDpi + ",inch="
|
||||
+ nInch + ",MovRange=" + nMoveRange);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
Log.i("0", "=========onConfigurationChanged ORIENTATION_LANDSCAPE");
|
||||
JSBridge.onOrientationChange();
|
||||
} else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
Log.i("0", "=========onConfigurationChanged ORIENTATION_PORTRAIT");
|
||||
JSBridge.onOrientationChange();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
if (requestCode == PermisionUtils.REQUEST_EXTERNAL_STORAGEP_ERMISSION_CODE) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
mLayaEngine.checkApkUpdate();
|
||||
}
|
||||
else {
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
|
||||
dialog.setTitle(R.string.waring);
|
||||
dialog.setMessage(R.string.permission_waring);
|
||||
dialog.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
mLayaEngine.checkApkUpdate();
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
static class layaGameListener implements ILayaEventListener{
|
||||
public Activity activity;
|
||||
@Override
|
||||
public void ExitGame() {
|
||||
Log.i("=======", "======exit");
|
||||
activity.finish();
|
||||
activity=null;
|
||||
//mLayaEngine.onDestroy();
|
||||
System.exit(0);
|
||||
}
|
||||
@Override
|
||||
public void destory() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package layaair.game.browser;
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
public class PermisionUtils {
|
||||
public static final int REQUEST_EXTERNAL_STORAGEP_ERMISSION_CODE = 1;
|
||||
public static boolean checkExternalStoragePermission(Context context) {
|
||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_EXTERNAL_STORAGEP_ERMISSION_CODE);
|
||||
}
|
||||
else if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_EXTERNAL_STORAGEP_ERMISSION_CODE);
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package layaair.game.browser;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.KeyEvent;
|
||||
import com.journeyapps.barcodescanner.CaptureManager;
|
||||
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
|
||||
import com.layabox.conch6.R;
|
||||
|
||||
/**
|
||||
* Created by lvfulong on 2018/1/15.
|
||||
*/
|
||||
|
||||
public class ScanActivity extends AppCompatActivity {
|
||||
private CaptureManager capture;
|
||||
private DecoratedBarcodeView barcodeScannerView;
|
||||
public static Activity m_instance = null;
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
m_instance = this;
|
||||
setContentView(R.layout.activity_scan);
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.my_awesome_toolbar);
|
||||
toolbar.setTitle("");
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
barcodeScannerView = (DecoratedBarcodeView)findViewById(R.id.zxing_barcode_scanner);
|
||||
|
||||
capture = new CaptureManager(this, barcodeScannerView);
|
||||
capture.initializeFromIntent(getIntent(), savedInstanceState);
|
||||
capture.decode();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
capture.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
capture.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
capture.onDestroy();
|
||||
m_instance = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
capture.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSupportNavigateUp() {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
return barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package layaair.game.browser;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import com.layabox.conch6.R;
|
||||
|
||||
|
||||
public class SplashDialog extends Dialog {
|
||||
private Context mContext;
|
||||
private long mStartTime;
|
||||
private long mleastShowTime = 2;
|
||||
private TextView mTipsView;
|
||||
private String[] mTips = {};
|
||||
private int mFontColor;
|
||||
private int mIndex = 0;
|
||||
private int mPercent = 0;
|
||||
private View mLayout;
|
||||
Handler mSplashHandler = new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
super.handleMessage(message);
|
||||
switch(message.what) {
|
||||
case 0:
|
||||
int length = mTips.length;
|
||||
mSplashHandler.removeMessages(0);
|
||||
if (length > 0) {
|
||||
if (mIndex >= length) {
|
||||
mIndex = 0;
|
||||
}
|
||||
mTipsView.setText(mTips[mIndex] + "(" + mPercent + "%)");
|
||||
mIndex++;
|
||||
}
|
||||
mSplashHandler.sendEmptyMessageDelayed(0, 1000);
|
||||
break;
|
||||
case 1:
|
||||
mSplashHandler.removeMessages(0);
|
||||
mSplashHandler.removeMessages(1);
|
||||
SplashDialog.this.dismiss();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public SplashDialog(Context context) {
|
||||
super(context, R.style.Splash);
|
||||
mContext = context;
|
||||
}
|
||||
public void setTips(String[] tips) {
|
||||
mTips = tips;
|
||||
}
|
||||
public void setPercent(int percent) {
|
||||
mPercent = percent;
|
||||
if (mPercent > 100) {
|
||||
mPercent = 100;
|
||||
}
|
||||
if (mPercent < 0) {
|
||||
mPercent = 0;
|
||||
}
|
||||
int length = mTips.length;
|
||||
if (length > 0) {
|
||||
if (mIndex >= mTips.length) {
|
||||
mIndex = 0;
|
||||
}
|
||||
mTipsView.setText(mTips[mIndex] + "(" + mPercent + "%)");
|
||||
}
|
||||
if (mPercent == 100) {
|
||||
dismissSplash();
|
||||
}
|
||||
}
|
||||
public void setFontColor(int color) {
|
||||
mTipsView.setTextColor(color);
|
||||
}
|
||||
public void setBackgroundColor(int color) {
|
||||
mLayout.setBackgroundColor(color);
|
||||
}
|
||||
public void showTextInfo(boolean show) {
|
||||
if (show) {
|
||||
mTipsView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else {
|
||||
mTipsView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
public void showSplash() {
|
||||
this.show();
|
||||
mStartTime = System.currentTimeMillis();
|
||||
mSplashHandler.sendEmptyMessage(0);
|
||||
}
|
||||
public void dismissSplash() {
|
||||
long showTime = System.currentTimeMillis() - mStartTime;
|
||||
if (showTime >= mleastShowTime * 1000) {
|
||||
mSplashHandler.sendEmptyMessage(1);
|
||||
}
|
||||
else {
|
||||
mSplashHandler.sendEmptyMessageDelayed(1, (long) (this.mleastShowTime * 1000 - showTime));
|
||||
}
|
||||
}
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
setContentView(R.layout.splash_dialog);
|
||||
mTipsView = (TextView)findViewById(R.id.tipsView);
|
||||
mLayout = findViewById(R.id.layout);
|
||||
}
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
if(event.getKeyCode() == KeyEvent.KEYCODE_BACK){
|
||||
return true;
|
||||
}else {
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package layaair.game.floatmenu;
|
||||
|
||||
import com.layabox.conch6.R;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import layaair.game.browser.ConchJNI;
|
||||
import layaair.game.browser.LayaWrapper;
|
||||
|
||||
public class FloatPanel extends FrameLayout {
|
||||
private WindowManager mWindowManager;
|
||||
private WindowManager.LayoutParams mParams;
|
||||
private View mView;
|
||||
private Context mContext;
|
||||
private boolean mShow = false;
|
||||
public FloatPanel(Context context) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
|
||||
|
||||
LayoutInflater layout = (LayoutInflater)context.getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
mView = layout.inflate(R.layout.float_panel , null);
|
||||
|
||||
View panel = mView.findViewById(R.id.float_panel);
|
||||
|
||||
View refresh = panel.findViewById(R.id.refresh_btn);
|
||||
|
||||
View back = panel.findViewById(R.id.back_btn);
|
||||
|
||||
refresh.setOnClickListener(onClick);
|
||||
back.setOnClickListener(onClick);
|
||||
}
|
||||
public void show() {
|
||||
|
||||
mParams = new WindowManager.LayoutParams();
|
||||
mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
mParams.gravity = Gravity.LEFT | Gravity.TOP;
|
||||
mParams.format = PixelFormat.RGBA_8888;
|
||||
mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
|
||||
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, MeasureSpec.AT_MOST);
|
||||
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, MeasureSpec.AT_MOST);
|
||||
mView.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
int screenWidth = mWindowManager.getDefaultDisplay().getWidth();
|
||||
int screenHeight = mWindowManager.getDefaultDisplay().getHeight();
|
||||
if (mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
mParams.x = screenWidth - mView.getMeasuredWidth() - 30;
|
||||
mParams.y = 30;
|
||||
}
|
||||
if (mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
mParams.x = screenWidth - mView.getMeasuredWidth() - 30;
|
||||
mParams.y = 30;
|
||||
}
|
||||
|
||||
try {
|
||||
mWindowManager.addView(mView, mParams);
|
||||
mShow = true;
|
||||
} catch (Exception e) {
|
||||
Log.d("", ">>>>>>>>>>>>>" + e.toString());
|
||||
}
|
||||
updatePosition();
|
||||
}
|
||||
public void hide() {
|
||||
if (mShow) {
|
||||
try {
|
||||
mWindowManager.removeView( mView );
|
||||
mShow = false;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void destory() {
|
||||
if (mShow) {
|
||||
mWindowManager.removeViewImmediate( mView );
|
||||
}
|
||||
}
|
||||
private static OnClickListener onClick = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.back_btn:
|
||||
String js = "window.conchConfig.JSDebugMode = 1;";
|
||||
ConchJNI.RunJS(js);
|
||||
LayaWrapper.onPopMenu(LayaWrapper.BACK_TO_MAIN);
|
||||
break;
|
||||
case R.id.refresh_btn:
|
||||
LayaWrapper.onPopMenu( LayaWrapper.REFRESH);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
public void updatePosition() {
|
||||
if (mShow) {
|
||||
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec( (1 << 30) - 1, MeasureSpec.AT_MOST );
|
||||
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec( (1 << 30) - 1, MeasureSpec.AT_MOST );
|
||||
mView.measure( widthMeasureSpec, heightMeasureSpec );
|
||||
int screenWidth = mWindowManager.getDefaultDisplay().getWidth();
|
||||
int screenHeight = mWindowManager.getDefaultDisplay().getHeight();
|
||||
if (mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
mParams.x = screenWidth - mView.getMeasuredWidth() - 30;
|
||||
mParams.y = 30;
|
||||
}
|
||||
if (mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
mParams.x = screenWidth - mView.getMeasuredWidth() - 30;
|
||||
mParams.y = 30;
|
||||
}
|
||||
mWindowManager.updateViewLayout( mView, mParams );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package layaair.game.wrapper;
|
||||
|
||||
import layaair.game.browser.LayaWrapper;
|
||||
|
||||
public class LayaWrapperFactroy {
|
||||
public static ILayaLibWrapper createLayaWrapper(){
|
||||
return new LayaWrapper();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true"
|
||||
android:drawable="@drawable/btn_back_pressed" /> <!-- pressed -->
|
||||
<item android:state_focused="true"
|
||||
android:drawable="@drawable/btn_back_focused" /> <!-- focused -->
|
||||
<item android:drawable="@drawable/btn_back_normal" /> <!-- default -->
|
||||
</selector>
|
||||
|
After Width: | Height: | Size: 670 B |
|
After Width: | Height: | Size: 408 B |
|
After Width: | Height: | Size: 543 B |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true"
|
||||
android:drawable="@drawable/btn_refresh_pressed" /> <!-- pressed -->
|
||||
<item android:state_focused="true"
|
||||
android:drawable="@drawable/btn_refresh_focused" /> <!-- focused -->
|
||||
<item android:drawable="@drawable/btn_refresh_normal" /> <!-- default -->
|
||||
</selector>
|
||||
|
After Width: | Height: | Size: 459 B |
|
After Width: | Height: | Size: 377 B |
|
After Width: | Height: | Size: 782 B |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 572 B |
@@ -0,0 +1,25 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/my_awesome_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
app:theme="@style/ThemeOverlay.AppCompat.ActionBar" />
|
||||
<com.journeyapps.barcodescanner.DecoratedBarcodeView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_below="@+id/my_awesome_toolbar"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:id="@+id/zxing_barcode_scanner"
|
||||
app:zxing_use_texture_view="true"/>
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/float_panel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/panel"
|
||||
android:clickable="false"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/refresh_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:contentDescription="TODO"
|
||||
android:background="#00000000"
|
||||
android:src="@drawable/btn_refresh" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/back_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:adjustViewBounds="false"
|
||||
android:background="#00000000"
|
||||
android:contentDescription="TODO"
|
||||
android:src="@drawable/btn_back" />
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logoView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:adjustViewBounds="true"
|
||||
android:maxHeight="200dp"
|
||||
android:maxWidth="200dp"
|
||||
android:src="@drawable/layabox"
|
||||
tools:scaleType="fitCenter" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tipsView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginBottom="68dp"
|
||||
android:text="" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="editText">#AAAAAA</color>
|
||||
<color name="blue">#008dff</color>
|
||||
<color name="gray">#bbbbbb</color>
|
||||
<color name="back_tv_color">#FF000000</color>
|
||||
<color name="plush_tv_color">#FF000000</color>
|
||||
<color name="cancel_tv_color">#FF000000</color>
|
||||
</resources>
|
||||