open source

This commit is contained in:
lvfulong
2020-11-11 16:17:13 +08:00
parent 4d989f3ecb
commit bc4ca748de
2441 changed files with 623057 additions and 2 deletions
+41
View File
@@ -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
+6
View File
@@ -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
File diff suppressed because it is too large Load Diff
@@ -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:&parameter];
} else {
[scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:&parameter];
}
}
}
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:@"&quot;"];
}
- (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
+85
View File
@@ -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
+37
View File
@@ -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>
+2
View File
@@ -0,0 +1,2 @@
Debug
Release
+20
View File
@@ -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>
File diff suppressed because it is too large Load Diff
@@ -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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

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>

Some files were not shown because too many files have changed in this diff Show More