├── .appveyor.yml ├── .eslintrc.yml ├── .gitignore ├── .jshintrc ├── .travis.yml ├── LICENSE ├── README.md ├── RELEASENOTES.md ├── package.json ├── plugin.xml ├── src ├── ios │ ├── CDVWKProcessPoolFactory.h │ ├── CDVWKProcessPoolFactory.m │ ├── CDVWKWebViewEngine.h │ ├── CDVWKWebViewEngine.m │ ├── CDVWKWebViewUIDelegate.h │ ├── CDVWKWebViewUIDelegate.m │ ├── GCDWebServer │ │ ├── Core │ │ │ ├── GCDWebServer.h │ │ │ ├── GCDWebServer.m │ │ │ ├── GCDWebServerConnection.h │ │ │ ├── GCDWebServerConnection.m │ │ │ ├── GCDWebServerFunctions.h │ │ │ ├── GCDWebServerFunctions.m │ │ │ ├── GCDWebServerHTTPStatusCodes.h │ │ │ ├── GCDWebServerPrivate.h │ │ │ ├── GCDWebServerRequest.h │ │ │ ├── GCDWebServerRequest.m │ │ │ ├── GCDWebServerResponse.h │ │ │ └── GCDWebServerResponse.m │ │ ├── Requests │ │ │ ├── GCDWebServerDataRequest.h │ │ │ ├── GCDWebServerDataRequest.m │ │ │ ├── GCDWebServerFileRequest.h │ │ │ ├── GCDWebServerFileRequest.m │ │ │ ├── GCDWebServerMultiPartFormRequest.h │ │ │ ├── GCDWebServerMultiPartFormRequest.m │ │ │ ├── GCDWebServerURLEncodedFormRequest.h │ │ │ └── GCDWebServerURLEncodedFormRequest.m │ │ └── Responses │ │ │ ├── GCDWebServerDataResponse.h │ │ │ ├── GCDWebServerDataResponse.m │ │ │ ├── GCDWebServerErrorResponse.h │ │ │ ├── GCDWebServerErrorResponse.m │ │ │ ├── GCDWebServerFileResponse.h │ │ │ ├── GCDWebServerFileResponse.m │ │ │ ├── GCDWebServerStreamedResponse.h │ │ │ └── GCDWebServerStreamedResponse.m │ ├── LICENSE │ └── wk-plugin.js └── www │ └── ios │ └── ios-wkwebview-exec.js └── tests ├── ios ├── CDVWKWebViewEngineTest.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── CDVWKWebViewEngineTest.xccheckout │ │ └── xcschemes │ │ └── CordovaLib.xcscheme ├── CDVWKWebViewEngineTest │ ├── .gitignore │ ├── CDVWKWebViewEngineLibTests │ │ ├── CDVWKWebViewEngineTest.m │ │ └── Info.plist │ └── CDVWKWebViewEngineTest.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── CDVWKWebViewEngineTest.xccheckout │ │ └── xcshareddata │ │ └── xcschemes │ │ ├── CDVWKWebViewEngineLib.xcscheme │ │ └── CDVWKWebViewEngineLibTests.xcscheme ├── README.md ├── package.json └── test.xcconfig ├── package.json ├── plugin.xml └── tests.js /.appveyor.yml: -------------------------------------------------------------------------------- 1 | # appveyor file 2 | # http://www.appveyor.com/docs/appveyor-yml 3 | 4 | max_jobs: 1 5 | 6 | shallow_clone: true 7 | 8 | init: 9 | - git config --global core.autocrlf true 10 | 11 | image: 12 | - Visual Studio 2017 13 | 14 | environment: 15 | nodejs_version: "4" 16 | matrix: 17 | - PLATFORM: windows-10-store 18 | 19 | install: 20 | - npm cache clean -f 21 | - node --version 22 | - npm install -g cordova-paramedic@https://github.com/apache/cordova-paramedic.git 23 | - npm install -g cordova 24 | 25 | build: off 26 | 27 | test_script: 28 | - cordova-paramedic --config pr\%PLATFORM% --plugin . --justBuild 29 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | root: true 2 | extends: semistandard 3 | rules: 4 | indent: 5 | - error 6 | - 4 7 | camelcase: off 8 | padded-blocks: off 9 | operator-linebreak: off 10 | no-throw-literal: off -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #If ignorance is bliss, then somebody knock the smile off my face 2 | 3 | *.csproj.user 4 | *.suo 5 | *.cache 6 | Thumbs.db 7 | *.DS_Store 8 | 9 | *.bak 10 | *.cache 11 | *.log 12 | *.swp 13 | *.user 14 | 15 | node_modules 16 | xcuserdata 17 | 18 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "browser": true 3 | , "devel": true 4 | , "bitwise": true 5 | , "undef": true 6 | , "trailing": true 7 | , "quotmark": false 8 | , "indent": 4 9 | , "unused": "vars" 10 | , "latedef": "nofunc" 11 | , "globals": { 12 | "module": false, 13 | "exports": false, 14 | "require": false 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode8 2 | language: objective-c 3 | sudo: false 4 | before_install: 5 | - rvm get head 6 | - npm cache clean -f 7 | - npm install -g n 8 | - n stable 9 | - node --version 10 | - xcodebuild -version 11 | install: 12 | - npm install 13 | script: 14 | - npm test 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | 3 | Repo moved to: [https://github.com/ionic-team/cordova-plugin-ionic-webview](https://github.com/ionic-team/cordova-plugin-ionic-webview) 4 | -------------------------------------------------------------------------------- /RELEASENOTES.md: -------------------------------------------------------------------------------- 1 | 21 | 22 | # Release Notes 23 | 24 | ### 1.1.3 (Apr 27, 2017) 25 | * [CB-12696](https://issues.apache.org/jira/browse/CB-12696) (iOS) Fixing some Xcode warnings 26 | * [CB-12685](https://issues.apache.org/jira/browse/CB-12685) added `package.json` to tests folder 27 | * [CB-12575](https://issues.apache.org/jira/browse/CB-12575) cordova-plugin-wkwebview-engine missing LICENSE file 28 | * [CB-12519](https://issues.apache.org/jira/browse/CB-12519) added missing license header 29 | 30 | ### 1.1.2 (Feb 28, 2017) 31 | * [CB-12497](https://issues.apache.org/jira/browse/CB-12497) `location.href` links are silently disallowed 32 | * [CB-12490](https://issues.apache.org/jira/browse/CB-12490) - Updated experimental plugin link 33 | * Allow to configure navigation by gestures 34 | * [CB-12297](https://issues.apache.org/jira/browse/CB-12297) Support `WKProcessPool` for cookie sharing 35 | * [CB-12414](https://issues.apache.org/jira/browse/CB-12414) **iOS:** Forward error from provisional load error to standard load error 36 | 37 | ### 1.1.1 (Dec 07, 2016) 38 | * [CB-12224](https://issues.apache.org/jira/browse/CB-12224) Updated version and RELEASENOTES.md for release 1.1.1 39 | * [CB-10228](https://issues.apache.org/jira/browse/CB-10228) - AppendUserAgent not working with WKWebView 40 | * [CB-11997](https://issues.apache.org/jira/browse/CB-11997) - Add crash recovery for iOS 8 41 | * [CB-11917](https://issues.apache.org/jira/browse/CB-11917) - Remove pull request template checklist item: "iCLA has been submitted…" 42 | * [CB-11818](https://issues.apache.org/jira/browse/CB-11818) - Avoid retain cycle: WKUserContentController retains its message handler, to break it we cannot pass directly CDVWKWebViewEngine's instance 43 | * [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Incremented plugin version. 44 | 45 | 46 | ### 1.1.0 (Sep 08, 2016) 47 | * [CB-11824](https://issues.apache.org/jira/browse/CB-11824) - Update tests to include objective-c tests 48 | * [CB-11554](https://issues.apache.org/jira/browse/CB-11554) - fixed unit tests 49 | * [CB-11815](https://issues.apache.org/jira/browse/CB-11815) (**iOS**) Fix hard-coded bridge name "cordova" 50 | * [CB-11554](https://issues.apache.org/jira/browse/CB-11554) - too 'brutal' app reload when title is empty 51 | * [CB-11074](https://issues.apache.org/jira/browse/CB-11074) - Ensure settings from `config.xml` are taken into consideration 52 | * Add ability to set the deceleration rate for the scrollview to 'fast' 53 | * [CB-11496](https://issues.apache.org/jira/browse/CB-11496) - Add obj-c unit tests for `WKWebViewConfiguration`, `WKPreference` 54 | * [CB-11496](https://issues.apache.org/jira/browse/CB-11496) - Create Obj-C unit-tests for `wkwebview-engine` (fix linker error) 55 | * [CB-11452](https://issues.apache.org/jira/browse/CB-11452) - Update README.md with latest news about `AllowInlineMediaPlayback` fix 56 | * [CB-9888](https://issues.apache.org/jira/browse/CB-9888) (**iOS**) check & reload `WKWebView` 57 | * [CB-11375](https://issues.apache.org/jira/browse/CB-11375) - `onReset` method of `CDVPlugin` is never called 58 | * Add pull request template. 59 | * [CB-10818](https://issues.apache.org/jira/browse/CB-10818) - Support the scroll deceleration speed preference. 60 | * [CB-10817](https://issues.apache.org/jira/browse/CB-10817) - Will now reload the `webView` if a crash occurs 61 | 62 | ### 1.0.3 (Apr 15, 2016) 63 | * [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add `JSHint` for plugins 64 | 65 | ### 1.0.2 (Feb 09, 2016) 66 | * [CB-10269](https://issues.apache.org/jira/browse/CB-10269) - Replace cordova exec only when present in wkwebview 67 | * [CB-10202](https://issues.apache.org/jira/browse/CB-10202) - Add README quirk about WKWebview does not work with the AllowInlineMediaPlayback preference 68 | 69 | 70 | ### 1.0.1 (Dec 11, 2015) 71 | 72 | * [CB-10190](https://issues.apache.org/jira/browse/CB-10190) - WKWebView engine is not releasing the user-agent lock 73 | 74 | ### 1.0.0 (Dec 04, 2015) 75 | 76 | * [CB-10146](https://issues.apache.org/jira/browse/CB-10146) - Add to README WKWebViewEngine quirks that will affect migration from UIWebView 77 | * [CB-10133](https://issues.apache.org/jira/browse/CB-10133) - DataClone DOM Exception 25 thrown for postMessage 78 | * [CB-10106](https://issues.apache.org/jira/browse/CB-10106) - added bridge proxy 79 | * [CB-10107](https://issues.apache.org/jira/browse/CB-10107) - nativeEvalAndFetch called for all bridges 80 | * [CB-10106](https://issues.apache.org/jira/browse/CB-10106) - iOS bridges need to take into account bridge changes 81 | * [CB-10073](https://issues.apache.org/jira/browse/CB-10073) - WKWebViewEngine should post CDVPluginResetNotification 82 | * [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated RELEASENOTES to be newest to oldest 83 | * [CB-10002](https://issues.apache.org/jira/browse/CB-10002) - WKWebView should propagate shouldOverrideLoadWithRequest to plugins 84 | * [CB-9979](https://issues.apache.org/jira/browse/CB-9979) [CB-9972](https://issues.apache.org/jira/browse/CB-9972) Change ATS link to new link 85 | * [CB-9636](https://issues.apache.org/jira/browse/CB-9636) - Plugin should detect at runtime iOS 8 and use of file:// url and present an error 86 | * [CB-8839](https://issues.apache.org/jira/browse/CB-8839) - WKWebView ignores DisallowOverscroll preference 87 | * [CB-8556](https://issues.apache.org/jira/browse/CB-8556) - fix handleOpenURL for WKWebViewEngine plugin 88 | * [CB-8666](https://issues.apache.org/jira/browse/CB-8666) - Update CDVWKWebViewEngine plugin to use 4.0.x branch code 89 | 90 | 91 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-wkwebview-engine", 3 | "version": "1.1.6", 4 | "description": "The official Apache Cordova WKWebView Engine Plugin", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/ionic-team/cordova-plugin-wkwebview-engine" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/ionic-team/cordova-plugin-wkwebview-engine/issues" 12 | }, 13 | "keywords": [ 14 | "cordova", 15 | "wkwebview" 16 | ], 17 | "scripts": { 18 | "test": "npm run eslint && npm run objc-tests", 19 | "objc-tests": "cd tests/ios && npm test", 20 | "preobjc-tests": "cd tests/ios && npm install", 21 | "eslint": "node_modules/.bin/eslint src" 22 | }, 23 | "author": "Apache Cordova", 24 | "license": "Apache-2.0", 25 | "devDependencies": { 26 | "eslint": "^3.19.0", 27 | "eslint-config-semistandard": "^11.0.0", 28 | "eslint-config-standard": "^10.2.1", 29 | "eslint-plugin-import": "^2.3.0", 30 | "eslint-plugin-node": "^5.0.0", 31 | "eslint-plugin-promise": "^3.5.0", 32 | "eslint-plugin-standard": "^3.0.1" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | 27 | Cordova WKWebView Engine 28 | Cordova WKWebView Engine Plugin 29 | Apache 2.0 30 | cordova,wkwebview,webview 31 | https://git-wip-us.apache.org/repos/asf/cordova-plugin-wkwebview-engine.git 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/ios/CDVWKProcessPoolFactory.h: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | #import 21 | 22 | @interface CDVWKProcessPoolFactory : NSObject 23 | @property (nonatomic, retain) WKProcessPool* sharedPool; 24 | 25 | +(instancetype) sharedFactory; 26 | -(WKProcessPool*) sharedProcessPool; 27 | @end 28 | -------------------------------------------------------------------------------- /src/ios/CDVWKProcessPoolFactory.m: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | #import 21 | #import 22 | #import "CDVWKProcessPoolFactory.h" 23 | 24 | static CDVWKProcessPoolFactory *factory = nil; 25 | 26 | @implementation CDVWKProcessPoolFactory 27 | 28 | + (instancetype)sharedFactory 29 | { 30 | static dispatch_once_t onceToken; 31 | dispatch_once(&onceToken, ^{ 32 | factory = [[CDVWKProcessPoolFactory alloc] init]; 33 | }); 34 | 35 | return factory; 36 | } 37 | 38 | - (instancetype)init 39 | { 40 | if (self = [super init]) { 41 | _sharedPool = [[WKProcessPool alloc] init]; 42 | } 43 | return self; 44 | } 45 | 46 | - (WKProcessPool*) sharedProcessPool { 47 | return _sharedPool; 48 | } 49 | @end 50 | -------------------------------------------------------------------------------- /src/ios/CDVWKWebViewEngine.h: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | #import 21 | #import 22 | 23 | @interface CDVWKWebViewEngine : CDVPlugin 24 | 25 | @property (nonatomic, strong, readonly) id uiDelegate; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /src/ios/CDVWKWebViewUIDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | #import 21 | 22 | @interface CDVWKWebViewUIDelegate : NSObject 23 | 24 | @property (nonatomic, copy) NSString* title; 25 | 26 | - (instancetype)initWithTitle:(NSString*)title; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /src/ios/CDVWKWebViewUIDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | #import "CDVWKWebViewUIDelegate.h" 21 | 22 | @implementation CDVWKWebViewUIDelegate 23 | 24 | - (instancetype)initWithTitle:(NSString*)title 25 | { 26 | self = [super init]; 27 | if (self) { 28 | self.title = title; 29 | } 30 | 31 | return self; 32 | } 33 | 34 | - (void) webView:(WKWebView*)webView runJavaScriptAlertPanelWithMessage:(NSString*)message 35 | initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(void))completionHandler 36 | { 37 | UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title 38 | message:message 39 | preferredStyle:UIAlertControllerStyleAlert]; 40 | 41 | UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK") 42 | style:UIAlertActionStyleDefault 43 | handler:^(UIAlertAction* action) 44 | { 45 | completionHandler(); 46 | [alert dismissViewControllerAnimated:YES completion:nil]; 47 | }]; 48 | 49 | [alert addAction:ok]; 50 | 51 | UIViewController* rootController = [UIApplication sharedApplication].delegate.window.rootViewController; 52 | 53 | [rootController presentViewController:alert animated:YES completion:nil]; 54 | } 55 | 56 | - (void) webView:(WKWebView*)webView runJavaScriptConfirmPanelWithMessage:(NSString*)message 57 | initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(BOOL result))completionHandler 58 | { 59 | UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title 60 | message:message 61 | preferredStyle:UIAlertControllerStyleAlert]; 62 | 63 | UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK") 64 | style:UIAlertActionStyleDefault 65 | handler:^(UIAlertAction* action) 66 | { 67 | completionHandler(YES); 68 | [alert dismissViewControllerAnimated:YES completion:nil]; 69 | }]; 70 | 71 | [alert addAction:ok]; 72 | 73 | UIAlertAction* cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel") 74 | style:UIAlertActionStyleDefault 75 | handler:^(UIAlertAction* action) 76 | { 77 | completionHandler(NO); 78 | [alert dismissViewControllerAnimated:YES completion:nil]; 79 | }]; 80 | [alert addAction:cancel]; 81 | 82 | UIViewController* rootController = [UIApplication sharedApplication].delegate.window.rootViewController; 83 | 84 | [rootController presentViewController:alert animated:YES completion:nil]; 85 | } 86 | 87 | - (void) webView:(WKWebView*)webView runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt 88 | defaultText:(NSString*)defaultText initiatedByFrame:(WKFrameInfo*)frame 89 | completionHandler:(void (^)(NSString* result))completionHandler 90 | { 91 | UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title 92 | message:prompt 93 | preferredStyle:UIAlertControllerStyleAlert]; 94 | 95 | UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK") 96 | style:UIAlertActionStyleDefault 97 | handler:^(UIAlertAction* action) 98 | { 99 | completionHandler(((UITextField*)alert.textFields[0]).text); 100 | [alert dismissViewControllerAnimated:YES completion:nil]; 101 | }]; 102 | 103 | [alert addAction:ok]; 104 | 105 | UIAlertAction* cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel") 106 | style:UIAlertActionStyleDefault 107 | handler:^(UIAlertAction* action) 108 | { 109 | completionHandler(nil); 110 | [alert dismissViewControllerAnimated:YES completion:nil]; 111 | }]; 112 | [alert addAction:cancel]; 113 | 114 | [alert addTextFieldWithConfigurationHandler:^(UITextField* textField) { 115 | textField.text = defaultText; 116 | }]; 117 | 118 | UIViewController* rootController = [UIApplication sharedApplication].delegate.window.rootViewController; 119 | 120 | [rootController presentViewController:alert animated:YES completion:nil]; 121 | } 122 | 123 | @end 124 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Core/GCDWebServerConnection.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServer.h" 29 | 30 | @class GCDWebServerHandler; 31 | 32 | /** 33 | * The GCDWebServerConnection class is instantiated by GCDWebServer to handle 34 | * each new HTTP connection. Each instance stays alive until the connection is 35 | * closed. 36 | * 37 | * You cannot use this class directly, but it is made public so you can 38 | * subclass it to override some hooks. Use the GCDWebServerOption_ConnectionClass 39 | * option for GCDWebServer to install your custom subclass. 40 | * 41 | * @warning The GCDWebServerConnection retains the GCDWebServer until the 42 | * connection is closed. 43 | */ 44 | @interface GCDWebServerConnection : NSObject 45 | 46 | /** 47 | * Returns the GCDWebServer that owns the connection. 48 | */ 49 | @property(nonatomic, readonly) GCDWebServer* server; 50 | 51 | /** 52 | * Returns YES if the connection is using IPv6. 53 | */ 54 | @property(nonatomic, readonly, getter=isUsingIPv6) BOOL usingIPv6; 55 | 56 | /** 57 | * Returns the address of the local peer (i.e. server) of the connection 58 | * as a raw "struct sockaddr". 59 | */ 60 | @property(nonatomic, readonly) NSData* localAddressData; 61 | 62 | /** 63 | * Returns the address of the local peer (i.e. server) of the connection 64 | * as a string. 65 | */ 66 | @property(nonatomic, readonly) NSString* localAddressString; 67 | 68 | /** 69 | * Returns the address of the remote peer (i.e. client) of the connection 70 | * as a raw "struct sockaddr". 71 | */ 72 | @property(nonatomic, readonly) NSData* remoteAddressData; 73 | 74 | /** 75 | * Returns the address of the remote peer (i.e. client) of the connection 76 | * as a string. 77 | */ 78 | @property(nonatomic, readonly) NSString* remoteAddressString; 79 | 80 | /** 81 | * Returns the total number of bytes received from the remote peer (i.e. client) 82 | * so far. 83 | */ 84 | @property(nonatomic, readonly) NSUInteger totalBytesRead; 85 | 86 | /** 87 | * Returns the total number of bytes sent to the remote peer (i.e. client) so far. 88 | */ 89 | @property(nonatomic, readonly) NSUInteger totalBytesWritten; 90 | 91 | @end 92 | 93 | /** 94 | * Hooks to customize the behavior of GCDWebServer HTTP connections. 95 | * 96 | * @warning These methods can be called on any GCD thread. 97 | * Be sure to also call "super" when overriding them. 98 | */ 99 | @interface GCDWebServerConnection (Subclassing) 100 | 101 | /** 102 | * This method is called when the connection is opened. 103 | * 104 | * Return NO to reject the connection e.g. after validating the local 105 | * or remote address. 106 | */ 107 | - (BOOL)open; 108 | 109 | /** 110 | * This method is called whenever data has been received 111 | * from the remote peer (i.e. client). 112 | * 113 | * @warning Do not attempt to modify this data. 114 | */ 115 | - (void)didReadBytes:(const void*)bytes length:(NSUInteger)length; 116 | 117 | /** 118 | * This method is called whenever data has been sent 119 | * to the remote peer (i.e. client). 120 | * 121 | * @warning Do not attempt to modify this data. 122 | */ 123 | - (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length; 124 | 125 | /** 126 | * This method is called after the HTTP headers have been received to 127 | * allow replacing the request URL by another one. 128 | * 129 | * The default implementation returns the original URL. 130 | */ 131 | - (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary*)headers; 132 | 133 | /** 134 | * Assuming a valid HTTP request was received, this method is called before 135 | * the request is processed. 136 | * 137 | * Return a non-nil GCDWebServerResponse to bypass the request processing entirely. 138 | * 139 | * The default implementation checks for HTTP authentication if applicable 140 | * and returns a barebone 401 status code response if authentication failed. 141 | */ 142 | - (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request; 143 | 144 | /** 145 | * Assuming a valid HTTP request was received and -preflightRequest: returned nil, 146 | * this method is called to process the request by executing the handler's 147 | * process block. 148 | */ 149 | - (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion; 150 | 151 | /** 152 | * Assuming a valid HTTP request was received and either -preflightRequest: 153 | * or -processRequest:completion: returned a non-nil GCDWebServerResponse, 154 | * this method is called to override the response. 155 | * 156 | * You can either modify the current response and return it, or return a 157 | * completely new one. 158 | * 159 | * The default implementation replaces any response matching the "ETag" or 160 | * "Last-Modified-Date" header of the request by a barebone "Not-Modified" (304) 161 | * one. 162 | */ 163 | - (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request; 164 | 165 | /** 166 | * This method is called if any error happens while validing or processing 167 | * the request or if no GCDWebServerResponse was generated during processing. 168 | * 169 | * @warning If the request was invalid (e.g. the HTTP headers were malformed), 170 | * the "request" argument will be nil. 171 | */ 172 | - (void)abortRequest:(GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode; 173 | 174 | /** 175 | * Called when the connection is closed. 176 | */ 177 | - (void)close; 178 | 179 | @end 180 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Core/GCDWebServerFunctions.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | /** 35 | * Converts a file extension to the corresponding MIME type. 36 | * If there is no match, "application/octet-stream" is returned. 37 | */ 38 | NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension); 39 | 40 | /** 41 | * Add percent-escapes to a string so it can be used in a URL. 42 | * The legal characters ":@/?&=+" are also escaped to ensure compatibility 43 | * with URL encoded forms and URL queries. 44 | */ 45 | NSString* GCDWebServerEscapeURLString(NSString* string); 46 | 47 | /** 48 | * Unescapes a URL percent-encoded string. 49 | */ 50 | NSString* GCDWebServerUnescapeURLString(NSString* string); 51 | 52 | /** 53 | * Extracts the unescaped names and values from an 54 | * "application/x-www-form-urlencoded" form. 55 | * http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 56 | */ 57 | NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form); 58 | 59 | /** 60 | * On OS X, returns the IPv4 or IPv6 address as a string of the primary 61 | * connected service or nil if not available. 62 | * 63 | * On iOS, returns the IPv4 or IPv6 address as a string of the WiFi 64 | * interface if connected or nil otherwise. 65 | */ 66 | NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6); 67 | 68 | /** 69 | * Converts a date into a string using RFC822 formatting. 70 | * https://tools.ietf.org/html/rfc822#section-5 71 | * https://tools.ietf.org/html/rfc1123#section-5.2.14 72 | */ 73 | NSString* GCDWebServerFormatRFC822(NSDate* date); 74 | 75 | /** 76 | * Converts a RFC822 formatted string into a date. 77 | * https://tools.ietf.org/html/rfc822#section-5 78 | * https://tools.ietf.org/html/rfc1123#section-5.2.14 79 | * 80 | * @warning Timezones other than GMT are not supported by this function. 81 | */ 82 | NSDate* GCDWebServerParseRFC822(NSString* string); 83 | 84 | /** 85 | * Converts a date into a string using IOS 8601 formatting. 86 | * http://tools.ietf.org/html/rfc3339#section-5.6 87 | */ 88 | NSString* GCDWebServerFormatISO8601(NSDate* date); 89 | 90 | /** 91 | * Converts a ISO 8601 formatted string into a date. 92 | * http://tools.ietf.org/html/rfc3339#section-5.6 93 | * 94 | * @warning Only "calendar" variant is supported at this time and timezones 95 | * other than GMT are not supported either. 96 | */ 97 | NSDate* GCDWebServerParseISO8601(NSString* string); 98 | 99 | #ifdef __cplusplus 100 | } 101 | #endif 102 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Core/GCDWebServerFunctions.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import 33 | #if TARGET_OS_IPHONE 34 | #import 35 | #else 36 | #import 37 | #endif 38 | #import 39 | 40 | #import 41 | #import 42 | #import 43 | 44 | #import "GCDWebServerPrivate.h" 45 | 46 | static NSDateFormatter* _dateFormatterRFC822 = nil; 47 | static NSDateFormatter* _dateFormatterISO8601 = nil; 48 | static dispatch_queue_t _dateFormatterQueue = NULL; 49 | 50 | // TODO: Handle RFC 850 and ANSI C's asctime() format 51 | void GCDWebServerInitializeFunctions() { 52 | GWS_DCHECK([NSThread isMainThread]); // NSDateFormatter should be initialized on main thread 53 | if (_dateFormatterRFC822 == nil) { 54 | _dateFormatterRFC822 = [[NSDateFormatter alloc] init]; 55 | _dateFormatterRFC822.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; 56 | _dateFormatterRFC822.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'"; 57 | _dateFormatterRFC822.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; 58 | GWS_DCHECK(_dateFormatterRFC822); 59 | } 60 | if (_dateFormatterISO8601 == nil) { 61 | _dateFormatterISO8601 = [[NSDateFormatter alloc] init]; 62 | _dateFormatterISO8601.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; 63 | _dateFormatterISO8601.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'+00:00'"; 64 | _dateFormatterISO8601.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; 65 | GWS_DCHECK(_dateFormatterISO8601); 66 | } 67 | if (_dateFormatterQueue == NULL) { 68 | _dateFormatterQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 69 | GWS_DCHECK(_dateFormatterQueue); 70 | } 71 | } 72 | 73 | NSString* GCDWebServerNormalizeHeaderValue(NSString* value) { 74 | if (value) { 75 | NSRange range = [value rangeOfString:@";"]; // Assume part before ";" separator is case-insensitive 76 | if (range.location != NSNotFound) { 77 | value = [[[value substringToIndex:range.location] lowercaseString] stringByAppendingString:[value substringFromIndex:range.location]]; 78 | } else { 79 | value = [value lowercaseString]; 80 | } 81 | } 82 | return value; 83 | } 84 | 85 | NSString* GCDWebServerTruncateHeaderValue(NSString* value) { 86 | NSRange range = [value rangeOfString:@";"]; 87 | return range.location != NSNotFound ? [value substringToIndex:range.location] : value; 88 | } 89 | 90 | NSString* GCDWebServerExtractHeaderValueParameter(NSString* value, NSString* name) { 91 | NSString* parameter = nil; 92 | NSScanner* scanner = [[NSScanner alloc] initWithString:value]; 93 | [scanner setCaseSensitive:NO]; // Assume parameter names are case-insensitive 94 | NSString* string = [NSString stringWithFormat:@"%@=", name]; 95 | if ([scanner scanUpToString:string intoString:NULL]) { 96 | [scanner scanString:string intoString:NULL]; 97 | if ([scanner scanString:@"\"" intoString:NULL]) { 98 | [scanner scanUpToString:@"\"" intoString:¶meter]; 99 | } else { 100 | [scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:¶meter]; 101 | } 102 | } 103 | return parameter; 104 | } 105 | 106 | // http://www.w3schools.com/tags/ref_charactersets.asp 107 | NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset) { 108 | NSStringEncoding encoding = kCFStringEncodingInvalidId; 109 | if (charset) { 110 | encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)charset)); 111 | } 112 | return (encoding != kCFStringEncodingInvalidId ? encoding : NSUTF8StringEncoding); 113 | } 114 | 115 | NSString* GCDWebServerFormatRFC822(NSDate* date) { 116 | __block NSString* string; 117 | dispatch_sync(_dateFormatterQueue, ^{ 118 | string = [_dateFormatterRFC822 stringFromDate:date]; 119 | }); 120 | return string; 121 | } 122 | 123 | NSDate* GCDWebServerParseRFC822(NSString* string) { 124 | __block NSDate* date; 125 | dispatch_sync(_dateFormatterQueue, ^{ 126 | date = [_dateFormatterRFC822 dateFromString:string]; 127 | }); 128 | return date; 129 | } 130 | 131 | NSString* GCDWebServerFormatISO8601(NSDate* date) { 132 | __block NSString* string; 133 | dispatch_sync(_dateFormatterQueue, ^{ 134 | string = [_dateFormatterISO8601 stringFromDate:date]; 135 | }); 136 | return string; 137 | } 138 | 139 | NSDate* GCDWebServerParseISO8601(NSString* string) { 140 | __block NSDate* date; 141 | dispatch_sync(_dateFormatterQueue, ^{ 142 | date = [_dateFormatterISO8601 dateFromString:string]; 143 | }); 144 | return date; 145 | } 146 | 147 | BOOL GCDWebServerIsTextContentType(NSString* type) { 148 | return ([type hasPrefix:@"text/"] || [type hasPrefix:@"application/json"] || [type hasPrefix:@"application/xml"]); 149 | } 150 | 151 | NSString* GCDWebServerDescribeData(NSData* data, NSString* type) { 152 | if (GCDWebServerIsTextContentType(type)) { 153 | NSString* charset = GCDWebServerExtractHeaderValueParameter(type, @"charset"); 154 | NSString* string = [[NSString alloc] initWithData:data encoding:GCDWebServerStringEncodingFromCharset(charset)]; 155 | if (string) { 156 | return string; 157 | } 158 | } 159 | return [NSString stringWithFormat:@"<%lu bytes>", (unsigned long)data.length]; 160 | } 161 | 162 | NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension) { 163 | static NSDictionary* _overrides = nil; 164 | if (_overrides == nil) { 165 | _overrides = [[NSDictionary alloc] initWithObjectsAndKeys: 166 | @"text/css", @"css", 167 | nil]; 168 | } 169 | NSString* mimeType = nil; 170 | extension = [extension lowercaseString]; 171 | if (extension.length) { 172 | mimeType = [_overrides objectForKey:extension]; 173 | if (mimeType == nil) { 174 | CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL); 175 | if (uti) { 176 | mimeType = CFBridgingRelease(UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)); 177 | CFRelease(uti); 178 | } 179 | } 180 | } 181 | return mimeType ? mimeType : kGCDWebServerDefaultMimeType; 182 | } 183 | 184 | NSString* GCDWebServerEscapeURLString(NSString* string) { 185 | #pragma clang diagnostic push 186 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 187 | return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":@/?&=+"), kCFStringEncodingUTF8)); 188 | #pragma clang diagnostic pop 189 | } 190 | 191 | NSString* GCDWebServerUnescapeURLString(NSString* string) { 192 | #pragma clang diagnostic push 193 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 194 | return CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)string, CFSTR(""), kCFStringEncodingUTF8)); 195 | #pragma clang diagnostic pop 196 | } 197 | 198 | NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) { 199 | NSMutableDictionary* parameters = [NSMutableDictionary dictionary]; 200 | NSScanner* scanner = [[NSScanner alloc] initWithString:form]; 201 | [scanner setCharactersToBeSkipped:nil]; 202 | while (1) { 203 | NSString* key = nil; 204 | if (![scanner scanUpToString:@"=" intoString:&key] || [scanner isAtEnd]) { 205 | break; 206 | } 207 | [scanner setScanLocation:([scanner scanLocation] + 1)]; 208 | 209 | NSString* value = nil; 210 | [scanner scanUpToString:@"&" intoString:&value]; 211 | if (value == nil) { 212 | value = @""; 213 | } 214 | 215 | key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "]; 216 | NSString* unescapedKey = key ? GCDWebServerUnescapeURLString(key) : nil; 217 | value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "]; 218 | NSString* unescapedValue = value ? GCDWebServerUnescapeURLString(value) : nil; 219 | if (unescapedKey && unescapedValue) { 220 | [parameters setObject:unescapedValue forKey:unescapedKey]; 221 | } else { 222 | GWS_LOG_WARNING(@"Failed parsing URL encoded form for key \"%@\" and value \"%@\"", key, value); 223 | GWS_DNOT_REACHED(); 224 | } 225 | 226 | if ([scanner isAtEnd]) { 227 | break; 228 | } 229 | [scanner setScanLocation:([scanner scanLocation] + 1)]; 230 | } 231 | return parameters; 232 | } 233 | 234 | NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService) { 235 | NSString* string = nil; 236 | char hostBuffer[NI_MAXHOST]; 237 | char serviceBuffer[NI_MAXSERV]; 238 | if (getnameinfo(addr, addr->sa_len, hostBuffer, sizeof(hostBuffer), serviceBuffer, sizeof(serviceBuffer), NI_NUMERICHOST | NI_NUMERICSERV | NI_NOFQDN) >= 0) { 239 | string = includeService ? [NSString stringWithFormat:@"%s:%s", hostBuffer, serviceBuffer] : [NSString stringWithUTF8String:hostBuffer]; 240 | } else { 241 | GWS_DNOT_REACHED(); 242 | } 243 | return string; 244 | } 245 | 246 | NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) { 247 | NSString* address = nil; 248 | #if TARGET_OS_IPHONE 249 | #if !TARGET_IPHONE_SIMULATOR && !TARGET_OS_TV 250 | const char* primaryInterface = "en0"; // WiFi interface on iOS 251 | #endif 252 | #else 253 | const char* primaryInterface = NULL; 254 | SCDynamicStoreRef store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("GCDWebServer"), NULL, NULL); 255 | if (store) { 256 | CFPropertyListRef info = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4")); // There is no equivalent for IPv6 but the primary interface should be the same 257 | if (info) { 258 | primaryInterface = [[NSString stringWithString:[(__bridge NSDictionary*)info objectForKey:@"PrimaryInterface"]] UTF8String]; 259 | CFRelease(info); 260 | } 261 | CFRelease(store); 262 | } 263 | if (primaryInterface == NULL) { 264 | primaryInterface = "lo0"; 265 | } 266 | #endif 267 | struct ifaddrs* list; 268 | if (getifaddrs(&list) >= 0) { 269 | for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) { 270 | #if TARGET_IPHONE_SIMULATOR || TARGET_OS_TV 271 | // Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator 272 | // Assumption holds for Apple TV running tvOS 273 | if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1")) 274 | #else 275 | if (strcmp(ifap->ifa_name, primaryInterface)) 276 | #endif 277 | { 278 | continue; 279 | } 280 | if ((ifap->ifa_flags & IFF_UP) && ((!useIPv6 && (ifap->ifa_addr->sa_family == AF_INET)) || (useIPv6 && (ifap->ifa_addr->sa_family == AF_INET6)))) { 281 | address = GCDWebServerStringFromSockAddr(ifap->ifa_addr, NO); 282 | break; 283 | } 284 | } 285 | freeifaddrs(list); 286 | } 287 | return address; 288 | } 289 | 290 | NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) { 291 | va_list arguments; 292 | va_start(arguments, format); 293 | const char* string = [[[NSString alloc] initWithFormat:format arguments:arguments] UTF8String]; 294 | va_end(arguments); 295 | unsigned char md5[CC_MD5_DIGEST_LENGTH]; 296 | CC_MD5(string, (CC_LONG)strlen(string), md5); 297 | char buffer[2 * CC_MD5_DIGEST_LENGTH + 1]; 298 | for (int i = 0; i < CC_MD5_DIGEST_LENGTH; ++i) { 299 | unsigned char byte = md5[i]; 300 | unsigned char byteHi = (byte & 0xF0) >> 4; 301 | buffer[2 * i + 0] = byteHi >= 10 ? 'a' + byteHi - 10 : '0' + byteHi; 302 | unsigned char byteLo = byte & 0x0F; 303 | buffer[2 * i + 1] = byteLo >= 10 ? 'a' + byteLo - 10 : '0' + byteLo; 304 | } 305 | buffer[2 * CC_MD5_DIGEST_LENGTH] = 0; 306 | return [NSString stringWithUTF8String:buffer]; 307 | } 308 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Core/GCDWebServerHTTPStatusCodes.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 29 | // http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml 30 | 31 | #import 32 | 33 | /** 34 | * Convenience constants for "informational" HTTP status codes. 35 | */ 36 | typedef NS_ENUM(NSInteger, GCDWebServerInformationalHTTPStatusCode) { 37 | kGCDWebServerHTTPStatusCode_Continue = 100, 38 | kGCDWebServerHTTPStatusCode_SwitchingProtocols = 101, 39 | kGCDWebServerHTTPStatusCode_Processing = 102 40 | }; 41 | 42 | /** 43 | * Convenience constants for "successful" HTTP status codes. 44 | */ 45 | typedef NS_ENUM(NSInteger, GCDWebServerSuccessfulHTTPStatusCode) { 46 | kGCDWebServerHTTPStatusCode_OK = 200, 47 | kGCDWebServerHTTPStatusCode_Created = 201, 48 | kGCDWebServerHTTPStatusCode_Accepted = 202, 49 | kGCDWebServerHTTPStatusCode_NonAuthoritativeInformation = 203, 50 | kGCDWebServerHTTPStatusCode_NoContent = 204, 51 | kGCDWebServerHTTPStatusCode_ResetContent = 205, 52 | kGCDWebServerHTTPStatusCode_PartialContent = 206, 53 | kGCDWebServerHTTPStatusCode_MultiStatus = 207, 54 | kGCDWebServerHTTPStatusCode_AlreadyReported = 208 55 | }; 56 | 57 | /** 58 | * Convenience constants for "redirection" HTTP status codes. 59 | */ 60 | typedef NS_ENUM(NSInteger, GCDWebServerRedirectionHTTPStatusCode) { 61 | kGCDWebServerHTTPStatusCode_MultipleChoices = 300, 62 | kGCDWebServerHTTPStatusCode_MovedPermanently = 301, 63 | kGCDWebServerHTTPStatusCode_Found = 302, 64 | kGCDWebServerHTTPStatusCode_SeeOther = 303, 65 | kGCDWebServerHTTPStatusCode_NotModified = 304, 66 | kGCDWebServerHTTPStatusCode_UseProxy = 305, 67 | kGCDWebServerHTTPStatusCode_TemporaryRedirect = 307, 68 | kGCDWebServerHTTPStatusCode_PermanentRedirect = 308 69 | }; 70 | 71 | /** 72 | * Convenience constants for "client error" HTTP status codes. 73 | */ 74 | typedef NS_ENUM(NSInteger, GCDWebServerClientErrorHTTPStatusCode) { 75 | kGCDWebServerHTTPStatusCode_BadRequest = 400, 76 | kGCDWebServerHTTPStatusCode_Unauthorized = 401, 77 | kGCDWebServerHTTPStatusCode_PaymentRequired = 402, 78 | kGCDWebServerHTTPStatusCode_Forbidden = 403, 79 | kGCDWebServerHTTPStatusCode_NotFound = 404, 80 | kGCDWebServerHTTPStatusCode_MethodNotAllowed = 405, 81 | kGCDWebServerHTTPStatusCode_NotAcceptable = 406, 82 | kGCDWebServerHTTPStatusCode_ProxyAuthenticationRequired = 407, 83 | kGCDWebServerHTTPStatusCode_RequestTimeout = 408, 84 | kGCDWebServerHTTPStatusCode_Conflict = 409, 85 | kGCDWebServerHTTPStatusCode_Gone = 410, 86 | kGCDWebServerHTTPStatusCode_LengthRequired = 411, 87 | kGCDWebServerHTTPStatusCode_PreconditionFailed = 412, 88 | kGCDWebServerHTTPStatusCode_RequestEntityTooLarge = 413, 89 | kGCDWebServerHTTPStatusCode_RequestURITooLong = 414, 90 | kGCDWebServerHTTPStatusCode_UnsupportedMediaType = 415, 91 | kGCDWebServerHTTPStatusCode_RequestedRangeNotSatisfiable = 416, 92 | kGCDWebServerHTTPStatusCode_ExpectationFailed = 417, 93 | kGCDWebServerHTTPStatusCode_UnprocessableEntity = 422, 94 | kGCDWebServerHTTPStatusCode_Locked = 423, 95 | kGCDWebServerHTTPStatusCode_FailedDependency = 424, 96 | kGCDWebServerHTTPStatusCode_UpgradeRequired = 426, 97 | kGCDWebServerHTTPStatusCode_PreconditionRequired = 428, 98 | kGCDWebServerHTTPStatusCode_TooManyRequests = 429, 99 | kGCDWebServerHTTPStatusCode_RequestHeaderFieldsTooLarge = 431 100 | }; 101 | 102 | /** 103 | * Convenience constants for "server error" HTTP status codes. 104 | */ 105 | typedef NS_ENUM(NSInteger, GCDWebServerServerErrorHTTPStatusCode) { 106 | kGCDWebServerHTTPStatusCode_InternalServerError = 500, 107 | kGCDWebServerHTTPStatusCode_NotImplemented = 501, 108 | kGCDWebServerHTTPStatusCode_BadGateway = 502, 109 | kGCDWebServerHTTPStatusCode_ServiceUnavailable = 503, 110 | kGCDWebServerHTTPStatusCode_GatewayTimeout = 504, 111 | kGCDWebServerHTTPStatusCode_HTTPVersionNotSupported = 505, 112 | kGCDWebServerHTTPStatusCode_InsufficientStorage = 507, 113 | kGCDWebServerHTTPStatusCode_LoopDetected = 508, 114 | kGCDWebServerHTTPStatusCode_NotExtended = 510, 115 | kGCDWebServerHTTPStatusCode_NetworkAuthenticationRequired = 511 116 | }; 117 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Core/GCDWebServerPrivate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import 29 | #import 30 | 31 | /** 32 | * All GCDWebServer headers. 33 | */ 34 | 35 | #import "GCDWebServerHTTPStatusCodes.h" 36 | #import "GCDWebServerFunctions.h" 37 | 38 | #import "GCDWebServer.h" 39 | #import "GCDWebServerConnection.h" 40 | 41 | #import "GCDWebServerDataRequest.h" 42 | #import "GCDWebServerFileRequest.h" 43 | #import "GCDWebServerMultiPartFormRequest.h" 44 | #import "GCDWebServerURLEncodedFormRequest.h" 45 | 46 | #import "GCDWebServerDataResponse.h" 47 | #import "GCDWebServerErrorResponse.h" 48 | #import "GCDWebServerFileResponse.h" 49 | #import "GCDWebServerStreamedResponse.h" 50 | 51 | /** 52 | * Check if a custom logging facility should be used instead. 53 | */ 54 | 55 | #if defined(__GCDWEBSERVER_LOGGING_HEADER__) 56 | 57 | #define __GCDWEBSERVER_LOGGING_FACILITY_CUSTOM__ 58 | 59 | #import __GCDWEBSERVER_LOGGING_HEADER__ 60 | 61 | /** 62 | * Automatically detect if XLFacility is available and if so use it as a 63 | * logging facility. 64 | */ 65 | 66 | #elif defined(__has_include) && __has_include("XLFacilityMacros.h") 67 | 68 | #define __GCDWEBSERVER_LOGGING_FACILITY_XLFACILITY__ 69 | 70 | #undef XLOG_TAG 71 | #define XLOG_TAG @"gcdwebserver.internal" 72 | 73 | #import "XLFacilityMacros.h" 74 | 75 | #define GWS_LOG_DEBUG(...) XLOG_DEBUG(__VA_ARGS__) 76 | #define GWS_LOG_VERBOSE(...) XLOG_VERBOSE(__VA_ARGS__) 77 | #define GWS_LOG_INFO(...) XLOG_INFO(__VA_ARGS__) 78 | #define GWS_LOG_WARNING(...) XLOG_WARNING(__VA_ARGS__) 79 | #define GWS_LOG_ERROR(...) XLOG_ERROR(__VA_ARGS__) 80 | 81 | #define GWS_DCHECK(__CONDITION__) XLOG_DEBUG_CHECK(__CONDITION__) 82 | #define GWS_DNOT_REACHED() XLOG_DEBUG_UNREACHABLE() 83 | 84 | /** 85 | * Automatically detect if CocoaLumberJack is available and if so use 86 | * it as a logging facility. 87 | */ 88 | 89 | #elif defined(__has_include) && __has_include("CocoaLumberjack/CocoaLumberjack.h") 90 | 91 | #import 92 | 93 | #define __GCDWEBSERVER_LOGGING_FACILITY_COCOALUMBERJACK__ 94 | 95 | #undef LOG_LEVEL_DEF 96 | #define LOG_LEVEL_DEF GCDWebServerLogLevel 97 | extern DDLogLevel GCDWebServerLogLevel; 98 | 99 | #define GWS_LOG_DEBUG(...) DDLogDebug(__VA_ARGS__) 100 | #define GWS_LOG_VERBOSE(...) DDLogVerbose(__VA_ARGS__) 101 | #define GWS_LOG_INFO(...) DDLogInfo(__VA_ARGS__) 102 | #define GWS_LOG_WARNING(...) DDLogWarn(__VA_ARGS__) 103 | #define GWS_LOG_ERROR(...) DDLogError(__VA_ARGS__) 104 | 105 | /** 106 | * If all of the above fail, then use GCDWebServer built-in 107 | * logging facility. 108 | */ 109 | 110 | #else 111 | 112 | #define __GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__ 113 | 114 | typedef NS_ENUM(int, GCDWebServerLoggingLevel) { 115 | kGCDWebServerLoggingLevel_Debug = 0, 116 | kGCDWebServerLoggingLevel_Verbose, 117 | kGCDWebServerLoggingLevel_Info, 118 | kGCDWebServerLoggingLevel_Warning, 119 | kGCDWebServerLoggingLevel_Error 120 | }; 121 | 122 | extern GCDWebServerLoggingLevel GCDWebServerLogLevel; 123 | extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ...) NS_FORMAT_FUNCTION(2, 3); 124 | 125 | #if DEBUG 126 | #define GWS_LOG_DEBUG(...) \ 127 | do { \ 128 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Debug) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Debug, __VA_ARGS__); \ 129 | } while (0) 130 | #else 131 | #define GWS_LOG_DEBUG(...) 132 | #endif 133 | #define GWS_LOG_VERBOSE(...) \ 134 | do { \ 135 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Verbose) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Verbose, __VA_ARGS__); \ 136 | } while (0) 137 | #define GWS_LOG_INFO(...) \ 138 | do { \ 139 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Info) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Info, __VA_ARGS__); \ 140 | } while (0) 141 | #define GWS_LOG_WARNING(...) \ 142 | do { \ 143 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Warning) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Warning, __VA_ARGS__); \ 144 | } while (0) 145 | #define GWS_LOG_ERROR(...) \ 146 | do { \ 147 | if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Error) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Error, __VA_ARGS__); \ 148 | } while (0) 149 | 150 | #endif 151 | 152 | /** 153 | * Consistency check macros used when building Debug only. 154 | */ 155 | 156 | #if !defined(GWS_DCHECK) || !defined(GWS_DNOT_REACHED) 157 | 158 | #if DEBUG 159 | 160 | #define GWS_DCHECK(__CONDITION__) \ 161 | do { \ 162 | if (!(__CONDITION__)) { \ 163 | abort(); \ 164 | } \ 165 | } while (0) 166 | #define GWS_DNOT_REACHED() abort() 167 | 168 | #else 169 | 170 | #define GWS_DCHECK(__CONDITION__) 171 | #define GWS_DNOT_REACHED() 172 | 173 | #endif 174 | 175 | #endif 176 | 177 | /** 178 | * GCDWebServer internal constants and APIs. 179 | */ 180 | 181 | #define kGCDWebServerDefaultMimeType @"application/octet-stream" 182 | #define kGCDWebServerErrorDomain @"GCDWebServerErrorDomain" 183 | 184 | static inline BOOL GCDWebServerIsValidByteRange(NSRange range) { 185 | return ((range.location != NSUIntegerMax) || (range.length > 0)); 186 | } 187 | 188 | static inline NSError* GCDWebServerMakePosixError(int code) { 189 | return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : [NSString stringWithUTF8String:strerror(code)]}]; 190 | } 191 | 192 | extern void GCDWebServerInitializeFunctions(); 193 | extern NSString* GCDWebServerNormalizeHeaderValue(NSString* value); 194 | extern NSString* GCDWebServerTruncateHeaderValue(NSString* value); 195 | extern NSString* GCDWebServerExtractHeaderValueParameter(NSString* header, NSString* attribute); 196 | extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset); 197 | extern BOOL GCDWebServerIsTextContentType(NSString* type); 198 | extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType); 199 | extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2); 200 | extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService); 201 | 202 | @interface GCDWebServerConnection () 203 | - (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket; 204 | @end 205 | 206 | @interface GCDWebServer () 207 | @property(nonatomic, readonly) NSArray* handlers; 208 | @property(nonatomic, readonly) NSString* serverName; 209 | @property(nonatomic, readonly) NSString* authenticationRealm; 210 | @property(nonatomic, readonly) NSDictionary* authenticationBasicAccounts; 211 | @property(nonatomic, readonly) NSDictionary* authenticationDigestAccounts; 212 | @property(nonatomic, readonly) BOOL shouldAutomaticallyMapHEADToGET; 213 | @property(nonatomic, readonly) dispatch_queue_priority_t dispatchQueuePriority; 214 | - (void)willStartConnection:(GCDWebServerConnection*)connection; 215 | - (void)didEndConnection:(GCDWebServerConnection*)connection; 216 | @end 217 | 218 | @interface GCDWebServerHandler : NSObject 219 | @property(nonatomic, readonly) GCDWebServerMatchBlock matchBlock; 220 | @property(nonatomic, readonly) GCDWebServerAsyncProcessBlock asyncProcessBlock; 221 | @end 222 | 223 | @interface GCDWebServerRequest () 224 | @property(nonatomic, readonly) BOOL usesChunkedTransferEncoding; 225 | @property(nonatomic, readwrite) NSData* localAddressData; 226 | @property(nonatomic, readwrite) NSData* remoteAddressData; 227 | - (void)prepareForWriting; 228 | - (BOOL)performOpen:(NSError**)error; 229 | - (BOOL)performWriteData:(NSData*)data error:(NSError**)error; 230 | - (BOOL)performClose:(NSError**)error; 231 | - (void)setAttribute:(id)attribute forKey:(NSString*)key; 232 | @end 233 | 234 | @interface GCDWebServerResponse () 235 | @property(nonatomic, readonly) NSDictionary* additionalHeaders; 236 | @property(nonatomic, readonly) BOOL usesChunkedTransferEncoding; 237 | - (void)prepareForReading; 238 | - (BOOL)performOpen:(NSError**)error; 239 | - (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block; 240 | - (void)performClose; 241 | @end 242 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Core/GCDWebServerRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import 29 | 30 | /** 31 | * Attribute key to retrieve an NSArray containing NSStrings from a GCDWebServerRequest 32 | * with the contents of any regular expression captures done on the request path. 33 | * 34 | * @warning This attribute will only be set on the request if adding a handler using 35 | * -addHandlerForMethod:pathRegex:requestClass:processBlock:. 36 | */ 37 | extern NSString* const GCDWebServerRequestAttribute_RegexCaptures; 38 | 39 | /** 40 | * This protocol is used by the GCDWebServerConnection to communicate with 41 | * the GCDWebServerRequest and write the received HTTP body data. 42 | * 43 | * Note that multiple GCDWebServerBodyWriter objects can be chained together 44 | * internally e.g. to automatically decode gzip encoded content before 45 | * passing it on to the GCDWebServerRequest. 46 | * 47 | * @warning These methods can be called on any GCD thread. 48 | */ 49 | @protocol GCDWebServerBodyWriter 50 | 51 | /** 52 | * This method is called before any body data is received. 53 | * 54 | * It should return YES on success or NO on failure and set the "error" argument 55 | * which is guaranteed to be non-NULL. 56 | */ 57 | - (BOOL)open:(NSError**)error; 58 | 59 | /** 60 | * This method is called whenever body data has been received. 61 | * 62 | * It should return YES on success or NO on failure and set the "error" argument 63 | * which is guaranteed to be non-NULL. 64 | */ 65 | - (BOOL)writeData:(NSData*)data error:(NSError**)error; 66 | 67 | /** 68 | * This method is called after all body data has been received. 69 | * 70 | * It should return YES on success or NO on failure and set the "error" argument 71 | * which is guaranteed to be non-NULL. 72 | */ 73 | - (BOOL)close:(NSError**)error; 74 | 75 | @end 76 | 77 | /** 78 | * The GCDWebServerRequest class is instantiated by the GCDWebServerConnection 79 | * after the HTTP headers have been received. Each instance wraps a single HTTP 80 | * request. If a body is present, the methods from the GCDWebServerBodyWriter 81 | * protocol will be called by the GCDWebServerConnection to receive it. 82 | * 83 | * The default implementation of the GCDWebServerBodyWriter protocol on the class 84 | * simply ignores the body data. 85 | * 86 | * @warning GCDWebServerRequest instances can be created and used on any GCD thread. 87 | */ 88 | @interface GCDWebServerRequest : NSObject 89 | 90 | /** 91 | * Returns the HTTP method for the request. 92 | */ 93 | @property(nonatomic, readonly) NSString* method; 94 | 95 | /** 96 | * Returns the URL for the request. 97 | */ 98 | @property(nonatomic, readonly) NSURL* URL; 99 | 100 | /** 101 | * Returns the HTTP headers for the request. 102 | */ 103 | @property(nonatomic, readonly) NSDictionary* headers; 104 | 105 | /** 106 | * Returns the path component of the URL for the request. 107 | */ 108 | @property(nonatomic, readonly) NSString* path; 109 | 110 | /** 111 | * Returns the parsed and unescaped query component of the URL for the request. 112 | * 113 | * @warning This property will be nil if there is no query in the URL. 114 | */ 115 | @property(nonatomic, readonly) NSDictionary* query; 116 | 117 | /** 118 | * Returns the content type for the body of the request parsed from the 119 | * "Content-Type" header. 120 | * 121 | * This property will be nil if the request has no body or set to 122 | * "application/octet-stream" if a body is present but there was no 123 | * "Content-Type" header. 124 | */ 125 | @property(nonatomic, readonly) NSString* contentType; 126 | 127 | /** 128 | * Returns the content length for the body of the request parsed from the 129 | * "Content-Length" header. 130 | * 131 | * This property will be set to "NSUIntegerMax" if the request has no body or 132 | * if there is a body but no "Content-Length" header, typically because 133 | * chunked transfer encoding is used. 134 | */ 135 | @property(nonatomic, readonly) NSUInteger contentLength; 136 | 137 | /** 138 | * Returns the parsed "If-Modified-Since" header or nil if absent or malformed. 139 | */ 140 | @property(nonatomic, readonly) NSDate* ifModifiedSince; 141 | 142 | /** 143 | * Returns the parsed "If-None-Match" header or nil if absent or malformed. 144 | */ 145 | @property(nonatomic, readonly) NSString* ifNoneMatch; 146 | 147 | /** 148 | * Returns the parsed "Range" header or (NSUIntegerMax, 0) if absent or malformed. 149 | * The range will be set to (offset, length) if expressed from the beginning 150 | * of the entity body, or (NSUIntegerMax, length) if expressed from its end. 151 | */ 152 | @property(nonatomic, readonly) NSRange byteRange; 153 | 154 | /** 155 | * Returns YES if the client supports gzip content encoding according to the 156 | * "Accept-Encoding" header. 157 | */ 158 | @property(nonatomic, readonly) BOOL acceptsGzipContentEncoding; 159 | 160 | /** 161 | * Returns the address of the local peer (i.e. server) for the request 162 | * as a raw "struct sockaddr". 163 | */ 164 | @property(nonatomic, readonly) NSData* localAddressData; 165 | 166 | /** 167 | * Returns the address of the local peer (i.e. server) for the request 168 | * as a string. 169 | */ 170 | @property(nonatomic, readonly) NSString* localAddressString; 171 | 172 | /** 173 | * Returns the address of the remote peer (i.e. client) for the request 174 | * as a raw "struct sockaddr". 175 | */ 176 | @property(nonatomic, readonly) NSData* remoteAddressData; 177 | 178 | /** 179 | * Returns the address of the remote peer (i.e. client) for the request 180 | * as a string. 181 | */ 182 | @property(nonatomic, readonly) NSString* remoteAddressString; 183 | 184 | /** 185 | * This method is the designated initializer for the class. 186 | */ 187 | - (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query; 188 | 189 | /** 190 | * Convenience method that checks if the contentType property is defined. 191 | */ 192 | - (BOOL)hasBody; 193 | 194 | /** 195 | * Convenience method that checks if the byteRange property is defined. 196 | */ 197 | - (BOOL)hasByteRange; 198 | 199 | /** 200 | * Retrieves an attribute associated with this request using the given key. 201 | * 202 | * @return The attribute value for the key. 203 | */ 204 | - (id)attributeForKey:(NSString*)key; 205 | 206 | @end 207 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Core/GCDWebServerRequest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import 33 | 34 | #import "GCDWebServerPrivate.h" 35 | 36 | NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerRequestAttribute_RegexCaptures"; 37 | 38 | #define kZlibErrorDomain @"ZlibErrorDomain" 39 | #define kGZipInitialBufferSize (256 * 1024) 40 | 41 | @interface GCDWebServerBodyDecoder : NSObject 42 | - (id)initWithRequest:(GCDWebServerRequest*)request writer:(id)writer; 43 | @end 44 | 45 | @interface GCDWebServerGZipDecoder : GCDWebServerBodyDecoder 46 | @end 47 | 48 | @interface GCDWebServerBodyDecoder () { 49 | @private 50 | GCDWebServerRequest* __unsafe_unretained _request; 51 | id __unsafe_unretained _writer; 52 | } 53 | @end 54 | 55 | @implementation GCDWebServerBodyDecoder 56 | 57 | - (id)initWithRequest:(GCDWebServerRequest*)request writer:(id)writer { 58 | if ((self = [super init])) { 59 | _request = request; 60 | _writer = writer; 61 | } 62 | return self; 63 | } 64 | 65 | - (BOOL)open:(NSError**)error { 66 | return [_writer open:error]; 67 | } 68 | 69 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 70 | return [_writer writeData:data error:error]; 71 | } 72 | 73 | - (BOOL)close:(NSError**)error { 74 | return [_writer close:error]; 75 | } 76 | 77 | @end 78 | 79 | @interface GCDWebServerGZipDecoder () { 80 | @private 81 | z_stream _stream; 82 | BOOL _finished; 83 | } 84 | @end 85 | 86 | @implementation GCDWebServerGZipDecoder 87 | 88 | - (BOOL)open:(NSError**)error { 89 | int result = inflateInit2(&_stream, 15 + 16); 90 | if (result != Z_OK) { 91 | if (error) { 92 | *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; 93 | } 94 | return NO; 95 | } 96 | if (![super open:error]) { 97 | inflateEnd(&_stream); 98 | return NO; 99 | } 100 | return YES; 101 | } 102 | 103 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 104 | GWS_DCHECK(!_finished); 105 | _stream.next_in = (Bytef*)data.bytes; 106 | _stream.avail_in = (uInt)data.length; 107 | NSMutableData* decodedData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize]; 108 | if (decodedData == nil) { 109 | GWS_DNOT_REACHED(); 110 | return NO; 111 | } 112 | NSUInteger length = 0; 113 | while (1) { 114 | NSUInteger maxLength = decodedData.length - length; 115 | _stream.next_out = (Bytef*)((char*)decodedData.mutableBytes + length); 116 | _stream.avail_out = (uInt)maxLength; 117 | int result = inflate(&_stream, Z_NO_FLUSH); 118 | if ((result != Z_OK) && (result != Z_STREAM_END)) { 119 | if (error) { 120 | *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; 121 | } 122 | return NO; 123 | } 124 | length += maxLength - _stream.avail_out; 125 | if (_stream.avail_out > 0) { 126 | if (result == Z_STREAM_END) { 127 | _finished = YES; 128 | } 129 | break; 130 | } 131 | decodedData.length = 2 * decodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available 132 | } 133 | decodedData.length = length; 134 | BOOL success = length ? [super writeData:decodedData error:error] : YES; // No need to call writer if we have no data yet 135 | return success; 136 | } 137 | 138 | - (BOOL)close:(NSError**)error { 139 | GWS_DCHECK(_finished); 140 | inflateEnd(&_stream); 141 | return [super close:error]; 142 | } 143 | 144 | @end 145 | 146 | @interface GCDWebServerRequest () { 147 | @private 148 | NSString* _method; 149 | NSURL* _url; 150 | NSDictionary* _headers; 151 | NSString* _path; 152 | NSDictionary* _query; 153 | NSString* _type; 154 | BOOL _chunked; 155 | NSUInteger _length; 156 | NSDate* _modifiedSince; 157 | NSString* _noneMatch; 158 | NSRange _range; 159 | BOOL _gzipAccepted; 160 | NSData* _localAddress; 161 | NSData* _remoteAddress; 162 | 163 | BOOL _opened; 164 | NSMutableArray* _decoders; 165 | NSMutableDictionary* _attributes; 166 | id __unsafe_unretained _writer; 167 | } 168 | @end 169 | 170 | @implementation GCDWebServerRequest : NSObject 171 | 172 | @synthesize method = _method, URL = _url, headers = _headers, path = _path, query = _query, contentType = _type, contentLength = _length, ifModifiedSince = _modifiedSince, ifNoneMatch = _noneMatch, 173 | byteRange = _range, acceptsGzipContentEncoding = _gzipAccepted, usesChunkedTransferEncoding = _chunked, localAddressData = _localAddress, remoteAddressData = _remoteAddress; 174 | 175 | - (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { 176 | if ((self = [super init])) { 177 | _method = [method copy]; 178 | _url = url; 179 | _headers = headers; 180 | _path = [path copy]; 181 | _query = query; 182 | 183 | _type = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Content-Type"]); 184 | _chunked = [GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Transfer-Encoding"]) isEqualToString:@"chunked"]; 185 | NSString* lengthHeader = [_headers objectForKey:@"Content-Length"]; 186 | if (lengthHeader) { 187 | NSInteger length = [lengthHeader integerValue]; 188 | if (_chunked || (length < 0)) { 189 | GWS_LOG_WARNING(@"Invalid 'Content-Length' header '%@' for '%@' request on \"%@\"", lengthHeader, _method, _url); 190 | GWS_DNOT_REACHED(); 191 | return nil; 192 | } 193 | _length = length; 194 | if (_type == nil) { 195 | _type = kGCDWebServerDefaultMimeType; 196 | } 197 | } else if (_chunked) { 198 | if (_type == nil) { 199 | _type = kGCDWebServerDefaultMimeType; 200 | } 201 | _length = NSUIntegerMax; 202 | } else { 203 | if (_type) { 204 | GWS_LOG_WARNING(@"Ignoring 'Content-Type' header for '%@' request on \"%@\"", _method, _url); 205 | _type = nil; // Content-Type without Content-Length or chunked-encoding doesn't make sense 206 | } 207 | _length = NSUIntegerMax; 208 | } 209 | 210 | NSString* modifiedHeader = [_headers objectForKey:@"If-Modified-Since"]; 211 | if (modifiedHeader) { 212 | _modifiedSince = [GCDWebServerParseRFC822(modifiedHeader) copy]; 213 | } 214 | _noneMatch = [_headers objectForKey:@"If-None-Match"]; 215 | 216 | _range = NSMakeRange(NSUIntegerMax, 0); 217 | NSString* rangeHeader = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Range"]); 218 | if (rangeHeader) { 219 | if ([rangeHeader hasPrefix:@"bytes="]) { 220 | NSArray* components = [[rangeHeader substringFromIndex:6] componentsSeparatedByString:@","]; 221 | if (components.count == 1) { 222 | components = [[components firstObject] componentsSeparatedByString:@"-"]; 223 | if (components.count == 2) { 224 | NSString* startString = [components objectAtIndex:0]; 225 | NSInteger startValue = [startString integerValue]; 226 | NSString* endString = [components objectAtIndex:1]; 227 | NSInteger endValue = [endString integerValue]; 228 | if (startString.length && (startValue >= 0) && endString.length && (endValue >= startValue)) { // The second 500 bytes: "500-999" 229 | _range.location = startValue; 230 | _range.length = endValue - startValue + 1; 231 | } else if (startString.length && (startValue >= 0)) { // The bytes after 9500 bytes: "9500-" 232 | _range.location = startValue; 233 | _range.length = NSUIntegerMax; 234 | } else if (endString.length && (endValue > 0)) { // The final 500 bytes: "-500" 235 | _range.location = NSUIntegerMax; 236 | _range.length = endValue; 237 | } 238 | } 239 | } 240 | } 241 | if ((_range.location == NSUIntegerMax) && (_range.length == 0)) { // Ignore "Range" header if syntactically invalid 242 | GWS_LOG_WARNING(@"Failed to parse 'Range' header \"%@\" for url: %@", rangeHeader, url); 243 | } 244 | } 245 | 246 | if ([[_headers objectForKey:@"Accept-Encoding"] rangeOfString:@"gzip"].location != NSNotFound) { 247 | _gzipAccepted = YES; 248 | } 249 | 250 | _decoders = [[NSMutableArray alloc] init]; 251 | _attributes = [[NSMutableDictionary alloc] init]; 252 | } 253 | return self; 254 | } 255 | 256 | - (BOOL)hasBody { 257 | return _type ? YES : NO; 258 | } 259 | 260 | - (BOOL)hasByteRange { 261 | return GCDWebServerIsValidByteRange(_range); 262 | } 263 | 264 | - (id)attributeForKey:(NSString*)key { 265 | return [_attributes objectForKey:key]; 266 | } 267 | 268 | - (BOOL)open:(NSError**)error { 269 | return YES; 270 | } 271 | 272 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 273 | return YES; 274 | } 275 | 276 | - (BOOL)close:(NSError**)error { 277 | return YES; 278 | } 279 | 280 | - (void)prepareForWriting { 281 | _writer = self; 282 | if ([GCDWebServerNormalizeHeaderValue([self.headers objectForKey:@"Content-Encoding"]) isEqualToString:@"gzip"]) { 283 | GCDWebServerGZipDecoder* decoder = [[GCDWebServerGZipDecoder alloc] initWithRequest:self writer:_writer]; 284 | [_decoders addObject:decoder]; 285 | _writer = decoder; 286 | } 287 | } 288 | 289 | - (BOOL)performOpen:(NSError**)error { 290 | GWS_DCHECK(_type); 291 | GWS_DCHECK(_writer); 292 | if (_opened) { 293 | GWS_DNOT_REACHED(); 294 | return NO; 295 | } 296 | _opened = YES; 297 | return [_writer open:error]; 298 | } 299 | 300 | - (BOOL)performWriteData:(NSData*)data error:(NSError**)error { 301 | GWS_DCHECK(_opened); 302 | return [_writer writeData:data error:error]; 303 | } 304 | 305 | - (BOOL)performClose:(NSError**)error { 306 | GWS_DCHECK(_opened); 307 | return [_writer close:error]; 308 | } 309 | 310 | - (void)setAttribute:(id)attribute forKey:(NSString*)key { 311 | [_attributes setValue:attribute forKey:key]; 312 | } 313 | 314 | - (NSString*)localAddressString { 315 | return GCDWebServerStringFromSockAddr(_localAddress.bytes, YES); 316 | } 317 | 318 | - (NSString*)remoteAddressString { 319 | return GCDWebServerStringFromSockAddr(_remoteAddress.bytes, YES); 320 | } 321 | 322 | - (NSString*)description { 323 | NSMutableString* description = [NSMutableString stringWithFormat:@"%@ %@", _method, _path]; 324 | for (NSString* argument in [[_query allKeys] sortedArrayUsingSelector:@selector(compare:)]) { 325 | [description appendFormat:@"\n %@ = %@", argument, [_query objectForKey:argument]]; 326 | } 327 | [description appendString:@"\n"]; 328 | for (NSString* header in [[_headers allKeys] sortedArrayUsingSelector:@selector(compare:)]) { 329 | [description appendFormat:@"\n%@: %@", header, [_headers objectForKey:header]]; 330 | } 331 | return description; 332 | } 333 | 334 | @end 335 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Core/GCDWebServerResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import 29 | 30 | /** 31 | * The GCDWebServerBodyReaderCompletionBlock is passed by GCDWebServer to the 32 | * GCDWebServerBodyReader object when reading data from it asynchronously. 33 | */ 34 | typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* error); 35 | 36 | /** 37 | * This protocol is used by the GCDWebServerConnection to communicate with 38 | * the GCDWebServerResponse and read the HTTP body data to send. 39 | * 40 | * Note that multiple GCDWebServerBodyReader objects can be chained together 41 | * internally e.g. to automatically apply gzip encoding to the content before 42 | * passing it on to the GCDWebServerResponse. 43 | * 44 | * @warning These methods can be called on any GCD thread. 45 | */ 46 | @protocol GCDWebServerBodyReader 47 | 48 | @required 49 | 50 | /** 51 | * This method is called before any body data is sent. 52 | * 53 | * It should return YES on success or NO on failure and set the "error" argument 54 | * which is guaranteed to be non-NULL. 55 | */ 56 | - (BOOL)open:(NSError**)error; 57 | 58 | /** 59 | * This method is called whenever body data is sent. 60 | * 61 | * It should return a non-empty NSData if there is body data available, 62 | * or an empty NSData there is no more body data, or nil on error and set 63 | * the "error" argument which is guaranteed to be non-NULL. 64 | */ 65 | - (NSData*)readData:(NSError**)error; 66 | 67 | /** 68 | * This method is called after all body data has been sent. 69 | */ 70 | - (void)close; 71 | 72 | @optional 73 | 74 | /** 75 | * If this method is implemented, it will be preferred over -readData:. 76 | * 77 | * It must call the passed block when data is available, passing a non-empty 78 | * NSData if there is body data available, or an empty NSData there is no more 79 | * body data, or nil on error and pass an NSError along. 80 | */ 81 | - (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block; 82 | 83 | @end 84 | 85 | /** 86 | * The GCDWebServerResponse class is used to wrap a single HTTP response. 87 | * It is instantiated by the handler of the GCDWebServer that handled the request. 88 | * If a body is present, the methods from the GCDWebServerBodyReader protocol 89 | * will be called by the GCDWebServerConnection to send it. 90 | * 91 | * The default implementation of the GCDWebServerBodyReader protocol 92 | * on the class simply returns an empty body. 93 | * 94 | * @warning GCDWebServerResponse instances can be created and used on any GCD thread. 95 | */ 96 | @interface GCDWebServerResponse : NSObject 97 | 98 | /** 99 | * Sets the content type for the body of the response. 100 | * 101 | * The default value is nil i.e. the response has no body. 102 | * 103 | * @warning This property must be set if a body is present. 104 | */ 105 | @property(nonatomic, copy) NSString* contentType; 106 | 107 | /** 108 | * Sets the content length for the body of the response. If a body is present 109 | * but this property is set to "NSUIntegerMax", this means the length of the body 110 | * cannot be known ahead of time. Chunked transfer encoding will be 111 | * automatically enabled by the GCDWebServerConnection to comply with HTTP/1.1 112 | * specifications. 113 | * 114 | * The default value is "NSUIntegerMax" i.e. the response has no body or its length 115 | * is undefined. 116 | */ 117 | @property(nonatomic) NSUInteger contentLength; 118 | 119 | /** 120 | * Sets the HTTP status code for the response. 121 | * 122 | * The default value is 200 i.e. "OK". 123 | */ 124 | @property(nonatomic) NSInteger statusCode; 125 | 126 | /** 127 | * Sets the caching hint for the response using the "Cache-Control" header. 128 | * This value is expressed in seconds. 129 | * 130 | * The default value is 0 i.e. "no-cache". 131 | */ 132 | @property(nonatomic) NSUInteger cacheControlMaxAge; 133 | 134 | /** 135 | * Sets the last modified date for the response using the "Last-Modified" header. 136 | * 137 | * The default value is nil. 138 | */ 139 | @property(nonatomic, retain) NSDate* lastModifiedDate; 140 | 141 | /** 142 | * Sets the ETag for the response using the "ETag" header. 143 | * 144 | * The default value is nil. 145 | */ 146 | @property(nonatomic, copy) NSString* eTag; 147 | 148 | /** 149 | * Enables gzip encoding for the response body. 150 | * 151 | * The default value is NO. 152 | * 153 | * @warning Enabling gzip encoding will remove any "Content-Length" header 154 | * since the length of the body is not known anymore. The client will still 155 | * be able to determine the body length when connection is closed per 156 | * HTTP/1.1 specifications. 157 | */ 158 | @property(nonatomic, getter=isGZipContentEncodingEnabled) BOOL gzipContentEncodingEnabled; 159 | 160 | /** 161 | * Creates an empty response. 162 | */ 163 | + (instancetype)response; 164 | 165 | /** 166 | * This method is the designated initializer for the class. 167 | */ 168 | - (instancetype)init; 169 | 170 | /** 171 | * Sets an additional HTTP header on the response. 172 | * Pass a nil value to remove an additional header. 173 | * 174 | * @warning Do not attempt to override the primary headers used 175 | * by GCDWebServerResponse like "Content-Type", "ETag", etc... 176 | */ 177 | - (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header; 178 | 179 | /** 180 | * Convenience method that checks if the contentType property is defined. 181 | */ 182 | - (BOOL)hasBody; 183 | 184 | @end 185 | 186 | @interface GCDWebServerResponse (Extensions) 187 | 188 | /** 189 | * Creates a empty response with a specific HTTP status code. 190 | */ 191 | + (instancetype)responseWithStatusCode:(NSInteger)statusCode; 192 | 193 | /** 194 | * Creates an HTTP redirect response to a new URL. 195 | */ 196 | + (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent; 197 | 198 | /** 199 | * Initializes an empty response with a specific HTTP status code. 200 | */ 201 | - (instancetype)initWithStatusCode:(NSInteger)statusCode; 202 | 203 | /** 204 | * Initializes an HTTP redirect response to a new URL. 205 | */ 206 | - (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent; 207 | 208 | @end 209 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Core/GCDWebServerResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import 33 | 34 | #import "GCDWebServerPrivate.h" 35 | 36 | #define kZlibErrorDomain @"ZlibErrorDomain" 37 | #define kGZipInitialBufferSize (256 * 1024) 38 | 39 | @interface GCDWebServerBodyEncoder : NSObject 40 | - (id)initWithResponse:(GCDWebServerResponse*)response reader:(id)reader; 41 | @end 42 | 43 | @interface GCDWebServerGZipEncoder : GCDWebServerBodyEncoder 44 | @end 45 | 46 | @interface GCDWebServerBodyEncoder () { 47 | @private 48 | GCDWebServerResponse* __unsafe_unretained _response; 49 | id __unsafe_unretained _reader; 50 | } 51 | @end 52 | 53 | @implementation GCDWebServerBodyEncoder 54 | 55 | - (id)initWithResponse:(GCDWebServerResponse*)response reader:(id)reader { 56 | if ((self = [super init])) { 57 | _response = response; 58 | _reader = reader; 59 | } 60 | return self; 61 | } 62 | 63 | - (BOOL)open:(NSError**)error { 64 | return [_reader open:error]; 65 | } 66 | 67 | - (NSData*)readData:(NSError**)error { 68 | return [_reader readData:error]; 69 | } 70 | 71 | - (void)close { 72 | [_reader close]; 73 | } 74 | 75 | @end 76 | 77 | @interface GCDWebServerGZipEncoder () { 78 | @private 79 | z_stream _stream; 80 | BOOL _finished; 81 | } 82 | @end 83 | 84 | @implementation GCDWebServerGZipEncoder 85 | 86 | - (id)initWithResponse:(GCDWebServerResponse*)response reader:(id)reader { 87 | if ((self = [super initWithResponse:response reader:reader])) { 88 | response.contentLength = NSUIntegerMax; // Make sure "Content-Length" header is not set since we don't know it 89 | [response setValue:@"gzip" forAdditionalHeader:@"Content-Encoding"]; 90 | } 91 | return self; 92 | } 93 | 94 | - (BOOL)open:(NSError**)error { 95 | int result = deflateInit2(&_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); 96 | if (result != Z_OK) { 97 | if (error) { 98 | *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; 99 | } 100 | return NO; 101 | } 102 | if (![super open:error]) { 103 | deflateEnd(&_stream); 104 | return NO; 105 | } 106 | return YES; 107 | } 108 | 109 | - (NSData*)readData:(NSError**)error { 110 | NSMutableData* encodedData; 111 | if (_finished) { 112 | encodedData = [[NSMutableData alloc] init]; 113 | } else { 114 | encodedData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize]; 115 | if (encodedData == nil) { 116 | GWS_DNOT_REACHED(); 117 | return nil; 118 | } 119 | NSUInteger length = 0; 120 | do { 121 | NSData* data = [super readData:error]; 122 | if (data == nil) { 123 | return nil; 124 | } 125 | _stream.next_in = (Bytef*)data.bytes; 126 | _stream.avail_in = (uInt)data.length; 127 | while (1) { 128 | NSUInteger maxLength = encodedData.length - length; 129 | _stream.next_out = (Bytef*)((char*)encodedData.mutableBytes + length); 130 | _stream.avail_out = (uInt)maxLength; 131 | int result = deflate(&_stream, data.length ? Z_NO_FLUSH : Z_FINISH); 132 | if (result == Z_STREAM_END) { 133 | _finished = YES; 134 | } else if (result != Z_OK) { 135 | if (error) { 136 | *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; 137 | } 138 | return nil; 139 | } 140 | length += maxLength - _stream.avail_out; 141 | if (_stream.avail_out > 0) { 142 | break; 143 | } 144 | encodedData.length = 2 * encodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available 145 | } 146 | GWS_DCHECK(_stream.avail_in == 0); 147 | } while (length == 0); // Make sure we don't return an empty NSData if not in finished state 148 | encodedData.length = length; 149 | } 150 | return encodedData; 151 | } 152 | 153 | - (void)close { 154 | deflateEnd(&_stream); 155 | [super close]; 156 | } 157 | 158 | @end 159 | 160 | @interface GCDWebServerResponse () { 161 | @private 162 | NSString* _type; 163 | NSUInteger _length; 164 | NSInteger _status; 165 | NSUInteger _maxAge; 166 | NSDate* _lastModified; 167 | NSString* _eTag; 168 | NSMutableDictionary* _headers; 169 | BOOL _chunked; 170 | BOOL _gzipped; 171 | 172 | BOOL _opened; 173 | NSMutableArray* _encoders; 174 | id __unsafe_unretained _reader; 175 | } 176 | @end 177 | 178 | @implementation GCDWebServerResponse 179 | 180 | @synthesize contentType = _type, contentLength = _length, statusCode = _status, cacheControlMaxAge = _maxAge, lastModifiedDate = _lastModified, eTag = _eTag, 181 | gzipContentEncodingEnabled = _gzipped, additionalHeaders = _headers; 182 | 183 | + (instancetype)response { 184 | return [[[self class] alloc] init]; 185 | } 186 | 187 | - (instancetype)init { 188 | if ((self = [super init])) { 189 | _type = nil; 190 | _length = NSUIntegerMax; 191 | _status = kGCDWebServerHTTPStatusCode_OK; 192 | _maxAge = 0; 193 | _headers = [[NSMutableDictionary alloc] init]; 194 | _encoders = [[NSMutableArray alloc] init]; 195 | } 196 | return self; 197 | } 198 | 199 | - (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header { 200 | [_headers setValue:value forKey:header]; 201 | } 202 | 203 | - (BOOL)hasBody { 204 | return _type ? YES : NO; 205 | } 206 | 207 | - (BOOL)usesChunkedTransferEncoding { 208 | return (_type != nil) && (_length == NSUIntegerMax); 209 | } 210 | 211 | - (BOOL)open:(NSError**)error { 212 | return YES; 213 | } 214 | 215 | - (NSData*)readData:(NSError**)error { 216 | return [NSData data]; 217 | } 218 | 219 | - (void)close { 220 | ; 221 | } 222 | 223 | - (void)prepareForReading { 224 | _reader = self; 225 | if (_gzipped) { 226 | GCDWebServerGZipEncoder* encoder = [[GCDWebServerGZipEncoder alloc] initWithResponse:self reader:_reader]; 227 | [_encoders addObject:encoder]; 228 | _reader = encoder; 229 | } 230 | } 231 | 232 | - (BOOL)performOpen:(NSError**)error { 233 | GWS_DCHECK(_type); 234 | GWS_DCHECK(_reader); 235 | if (_opened) { 236 | GWS_DNOT_REACHED(); 237 | return NO; 238 | } 239 | _opened = YES; 240 | return [_reader open:error]; 241 | } 242 | 243 | - (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block { 244 | GWS_DCHECK(_opened); 245 | if ([_reader respondsToSelector:@selector(asyncReadDataWithCompletion:)]) { 246 | [_reader asyncReadDataWithCompletion:[block copy]]; 247 | } else { 248 | NSError* error = nil; 249 | NSData* data = [_reader readData:&error]; 250 | block(data, error); 251 | } 252 | } 253 | 254 | - (void)performClose { 255 | GWS_DCHECK(_opened); 256 | [_reader close]; 257 | } 258 | 259 | - (NSString*)description { 260 | NSMutableString* description = [NSMutableString stringWithFormat:@"Status Code = %i", (int)_status]; 261 | if (_type) { 262 | [description appendFormat:@"\nContent Type = %@", _type]; 263 | } 264 | if (_length != NSUIntegerMax) { 265 | [description appendFormat:@"\nContent Length = %lu", (unsigned long)_length]; 266 | } 267 | [description appendFormat:@"\nCache Control Max Age = %lu", (unsigned long)_maxAge]; 268 | if (_lastModified) { 269 | [description appendFormat:@"\nLast Modified Date = %@", _lastModified]; 270 | } 271 | if (_eTag) { 272 | [description appendFormat:@"\nETag = %@", _eTag]; 273 | } 274 | if (_headers.count) { 275 | [description appendString:@"\n"]; 276 | for (NSString* header in [[_headers allKeys] sortedArrayUsingSelector:@selector(compare:)]) { 277 | [description appendFormat:@"\n%@: %@", header, [_headers objectForKey:header]]; 278 | } 279 | } 280 | return description; 281 | } 282 | 283 | @end 284 | 285 | @implementation GCDWebServerResponse (Extensions) 286 | 287 | + (instancetype)responseWithStatusCode:(NSInteger)statusCode { 288 | return [[self alloc] initWithStatusCode:statusCode]; 289 | } 290 | 291 | + (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent { 292 | return [[self alloc] initWithRedirect:location permanent:permanent]; 293 | } 294 | 295 | - (instancetype)initWithStatusCode:(NSInteger)statusCode { 296 | if ((self = [self init])) { 297 | self.statusCode = statusCode; 298 | } 299 | return self; 300 | } 301 | 302 | - (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent { 303 | if ((self = [self init])) { 304 | self.statusCode = permanent ? kGCDWebServerHTTPStatusCode_MovedPermanently : kGCDWebServerHTTPStatusCode_TemporaryRedirect; 305 | [self setValue:[location absoluteString] forAdditionalHeader:@"Location"]; 306 | } 307 | return self; 308 | } 309 | 310 | @end 311 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Requests/GCDWebServerDataRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerRequest.h" 29 | 30 | /** 31 | * The GCDWebServerDataRequest subclass of GCDWebServerRequest stores the body 32 | * of the HTTP request in memory. 33 | */ 34 | @interface GCDWebServerDataRequest : GCDWebServerRequest 35 | 36 | /** 37 | * Returns the data for the request body. 38 | */ 39 | @property(nonatomic, readonly) NSData* data; 40 | 41 | @end 42 | 43 | @interface GCDWebServerDataRequest (Extensions) 44 | 45 | /** 46 | * Returns the data for the request body interpreted as text. If the content 47 | * type of the body is not a text one, or if an error occurs, nil is returned. 48 | * 49 | * The text encoding used to interpret the data is extracted from the 50 | * "Content-Type" header or defaults to UTF-8. 51 | */ 52 | @property(nonatomic, readonly) NSString* text; 53 | 54 | /** 55 | * Returns the data for the request body interpreted as a JSON object. If the 56 | * content type of the body is not JSON, or if an error occurs, nil is returned. 57 | */ 58 | @property(nonatomic, readonly) id jsonObject; 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Requests/GCDWebServerDataRequest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @interface GCDWebServerDataRequest () { 35 | @private 36 | NSMutableData* _data; 37 | 38 | NSString* _text; 39 | id _jsonObject; 40 | } 41 | @end 42 | 43 | @implementation GCDWebServerDataRequest 44 | 45 | @synthesize data = _data; 46 | 47 | - (BOOL)open:(NSError**)error { 48 | if (self.contentLength != NSUIntegerMax) { 49 | _data = [[NSMutableData alloc] initWithCapacity:self.contentLength]; 50 | } else { 51 | _data = [[NSMutableData alloc] init]; 52 | } 53 | if (_data == nil) { 54 | if (error) { 55 | *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{ NSLocalizedDescriptionKey : @"Failed allocating memory" }]; 56 | } 57 | return NO; 58 | } 59 | return YES; 60 | } 61 | 62 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 63 | [_data appendData:data]; 64 | return YES; 65 | } 66 | 67 | - (BOOL)close:(NSError**)error { 68 | return YES; 69 | } 70 | 71 | - (NSString*)description { 72 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 73 | if (_data) { 74 | [description appendString:@"\n\n"]; 75 | [description appendString:GCDWebServerDescribeData(_data, self.contentType)]; 76 | } 77 | return description; 78 | } 79 | 80 | @end 81 | 82 | @implementation GCDWebServerDataRequest (Extensions) 83 | 84 | - (NSString*)text { 85 | if (_text == nil) { 86 | if ([self.contentType hasPrefix:@"text/"]) { 87 | NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); 88 | _text = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)]; 89 | } else { 90 | GWS_DNOT_REACHED(); 91 | } 92 | } 93 | return _text; 94 | } 95 | 96 | - (id)jsonObject { 97 | if (_jsonObject == nil) { 98 | NSString* mimeType = GCDWebServerTruncateHeaderValue(self.contentType); 99 | if ([mimeType isEqualToString:@"application/json"] || [mimeType isEqualToString:@"text/json"] || [mimeType isEqualToString:@"text/javascript"]) { 100 | _jsonObject = [NSJSONSerialization JSONObjectWithData:_data options:0 error:NULL]; 101 | } else { 102 | GWS_DNOT_REACHED(); 103 | } 104 | } 105 | return _jsonObject; 106 | } 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Requests/GCDWebServerFileRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerRequest.h" 29 | 30 | /** 31 | * The GCDWebServerFileRequest subclass of GCDWebServerRequest stores the body 32 | * of the HTTP request to a file on disk. 33 | */ 34 | @interface GCDWebServerFileRequest : GCDWebServerRequest 35 | 36 | /** 37 | * Returns the path to the temporary file containing the request body. 38 | * 39 | * @warning This temporary file will be automatically deleted when the 40 | * GCDWebServerFileRequest is deallocated. If you want to preserve this file, 41 | * you must move it to a different location beforehand. 42 | */ 43 | @property(nonatomic, readonly) NSString* temporaryPath; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Requests/GCDWebServerFileRequest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @interface GCDWebServerFileRequest () { 35 | @private 36 | NSString* _temporaryPath; 37 | int _file; 38 | } 39 | @end 40 | 41 | @implementation GCDWebServerFileRequest 42 | 43 | @synthesize temporaryPath = _temporaryPath; 44 | 45 | - (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { 46 | if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) { 47 | _temporaryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; 48 | } 49 | return self; 50 | } 51 | 52 | - (void)dealloc { 53 | unlink([_temporaryPath fileSystemRepresentation]); 54 | } 55 | 56 | - (BOOL)open:(NSError**)error { 57 | _file = open([_temporaryPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 58 | if (_file <= 0) { 59 | if (error) { 60 | *error = GCDWebServerMakePosixError(errno); 61 | } 62 | return NO; 63 | } 64 | return YES; 65 | } 66 | 67 | - (BOOL)writeData:(NSData*)data error:(NSError**)error { 68 | if (write(_file, data.bytes, data.length) != (ssize_t)data.length) { 69 | if (error) { 70 | *error = GCDWebServerMakePosixError(errno); 71 | } 72 | return NO; 73 | } 74 | return YES; 75 | } 76 | 77 | - (BOOL)close:(NSError**)error { 78 | if (close(_file) < 0) { 79 | if (error) { 80 | *error = GCDWebServerMakePosixError(errno); 81 | } 82 | return NO; 83 | } 84 | #ifdef __GCDWEBSERVER_ENABLE_TESTING__ 85 | NSString* creationDateHeader = [self.headers objectForKey:@"X-GCDWebServer-CreationDate"]; 86 | if (creationDateHeader) { 87 | NSDate* date = GCDWebServerParseISO8601(creationDateHeader); 88 | if (!date || ![[NSFileManager defaultManager] setAttributes:@{ NSFileCreationDate : date } ofItemAtPath:_temporaryPath error:error]) { 89 | return NO; 90 | } 91 | } 92 | NSString* modifiedDateHeader = [self.headers objectForKey:@"X-GCDWebServer-ModifiedDate"]; 93 | if (modifiedDateHeader) { 94 | NSDate* date = GCDWebServerParseRFC822(modifiedDateHeader); 95 | if (!date || ![[NSFileManager defaultManager] setAttributes:@{ NSFileModificationDate : date } ofItemAtPath:_temporaryPath error:error]) { 96 | return NO; 97 | } 98 | } 99 | #endif 100 | return YES; 101 | } 102 | 103 | - (NSString*)description { 104 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 105 | [description appendFormat:@"\n\n{%@}", _temporaryPath]; 106 | return description; 107 | } 108 | 109 | @end 110 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerRequest.h" 29 | 30 | /** 31 | * The GCDWebServerMultiPart class is an abstract class that wraps the content 32 | * of a part. 33 | */ 34 | @interface GCDWebServerMultiPart : NSObject 35 | 36 | /** 37 | * Returns the control name retrieved from the part headers. 38 | */ 39 | @property(nonatomic, readonly) NSString* controlName; 40 | 41 | /** 42 | * Returns the content type retrieved from the part headers or "text/plain" 43 | * if not available (per HTTP specifications). 44 | */ 45 | @property(nonatomic, readonly) NSString* contentType; 46 | 47 | /** 48 | * Returns the MIME type component of the content type for the part. 49 | */ 50 | @property(nonatomic, readonly) NSString* mimeType; 51 | 52 | @end 53 | 54 | /** 55 | * The GCDWebServerMultiPartArgument subclass of GCDWebServerMultiPart wraps 56 | * the content of a part as data in memory. 57 | */ 58 | @interface GCDWebServerMultiPartArgument : GCDWebServerMultiPart 59 | 60 | /** 61 | * Returns the data for the part. 62 | */ 63 | @property(nonatomic, readonly) NSData* data; 64 | 65 | /** 66 | * Returns the data for the part interpreted as text. If the content 67 | * type of the part is not a text one, or if an error occurs, nil is returned. 68 | * 69 | * The text encoding used to interpret the data is extracted from the 70 | * "Content-Type" header or defaults to UTF-8. 71 | */ 72 | @property(nonatomic, readonly) NSString* string; 73 | 74 | @end 75 | 76 | /** 77 | * The GCDWebServerMultiPartFile subclass of GCDWebServerMultiPart wraps 78 | * the content of a part as a file on disk. 79 | */ 80 | @interface GCDWebServerMultiPartFile : GCDWebServerMultiPart 81 | 82 | /** 83 | * Returns the file name retrieved from the part headers. 84 | */ 85 | @property(nonatomic, readonly) NSString* fileName; 86 | 87 | /** 88 | * Returns the path to the temporary file containing the part data. 89 | * 90 | * @warning This temporary file will be automatically deleted when the 91 | * GCDWebServerMultiPartFile is deallocated. If you want to preserve this file, 92 | * you must move it to a different location beforehand. 93 | */ 94 | @property(nonatomic, readonly) NSString* temporaryPath; 95 | 96 | @end 97 | 98 | /** 99 | * The GCDWebServerMultiPartFormRequest subclass of GCDWebServerRequest 100 | * parses the body of the HTTP request as a multipart encoded form. 101 | */ 102 | @interface GCDWebServerMultiPartFormRequest : GCDWebServerRequest 103 | 104 | /** 105 | * Returns the argument parts from the multipart encoded form as 106 | * name / GCDWebServerMultiPartArgument pairs. 107 | */ 108 | @property(nonatomic, readonly) NSArray* arguments; 109 | 110 | /** 111 | * Returns the files parts from the multipart encoded form as 112 | * name / GCDWebServerMultiPartFile pairs. 113 | */ 114 | @property(nonatomic, readonly) NSArray* files; 115 | 116 | /** 117 | * Returns the MIME type for multipart encoded forms 118 | * i.e. "multipart/form-data". 119 | */ 120 | + (NSString*)mimeType; 121 | 122 | /** 123 | * Returns the first argument for a given control name or nil if not found. 124 | */ 125 | - (GCDWebServerMultiPartArgument*)firstArgumentForControlName:(NSString*)name; 126 | 127 | /** 128 | * Returns the first file for a given control name or nil if not found. 129 | */ 130 | - (GCDWebServerMultiPartFile*)firstFileForControlName:(NSString*)name; 131 | 132 | @end 133 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerDataRequest.h" 29 | 30 | /** 31 | * The GCDWebServerURLEncodedFormRequest subclass of GCDWebServerRequest 32 | * parses the body of the HTTP request as a URL encoded form using 33 | * GCDWebServerParseURLEncodedForm(). 34 | */ 35 | @interface GCDWebServerURLEncodedFormRequest : GCDWebServerDataRequest 36 | 37 | /** 38 | * Returns the unescaped control names and values for the URL encoded form. 39 | * 40 | * The text encoding used to interpret the data is extracted from the 41 | * "Content-Type" header or defaults to UTF-8. 42 | */ 43 | @property(nonatomic, readonly) NSDictionary* arguments; 44 | 45 | /** 46 | * Returns the MIME type for URL encoded forms 47 | * i.e. "application/x-www-form-urlencoded". 48 | */ 49 | + (NSString*)mimeType; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Requests/GCDWebServerURLEncodedFormRequest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @interface GCDWebServerURLEncodedFormRequest () { 35 | @private 36 | NSDictionary* _arguments; 37 | } 38 | @end 39 | 40 | @implementation GCDWebServerURLEncodedFormRequest 41 | 42 | @synthesize arguments = _arguments; 43 | 44 | + (NSString*)mimeType { 45 | return @"application/x-www-form-urlencoded"; 46 | } 47 | 48 | - (BOOL)close:(NSError**)error { 49 | if (![super close:error]) { 50 | return NO; 51 | } 52 | 53 | NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); 54 | NSString* string = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)]; 55 | _arguments = GCDWebServerParseURLEncodedForm(string); 56 | GWS_DCHECK(_arguments); 57 | 58 | return YES; 59 | } 60 | 61 | - (NSString*)description { 62 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 63 | [description appendString:@"\n"]; 64 | for (NSString* argument in [[_arguments allKeys] sortedArrayUsingSelector:@selector(compare:)]) { 65 | [description appendFormat:@"\n%@ = %@", argument, [_arguments objectForKey:argument]]; 66 | } 67 | return description; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Responses/GCDWebServerDataResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerResponse.h" 29 | 30 | /** 31 | * The GCDWebServerDataResponse subclass of GCDWebServerResponse reads the body 32 | * of the HTTP response from memory. 33 | */ 34 | @interface GCDWebServerDataResponse : GCDWebServerResponse 35 | 36 | /** 37 | * Creates a response with data in memory and a given content type. 38 | */ 39 | + (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type; 40 | 41 | /** 42 | * This method is the designated initializer for the class. 43 | */ 44 | - (instancetype)initWithData:(NSData*)data contentType:(NSString*)type; 45 | 46 | @end 47 | 48 | @interface GCDWebServerDataResponse (Extensions) 49 | 50 | /** 51 | * Creates a data response from text encoded using UTF-8. 52 | */ 53 | + (instancetype)responseWithText:(NSString*)text; 54 | 55 | /** 56 | * Creates a data response from HTML encoded using UTF-8. 57 | */ 58 | + (instancetype)responseWithHTML:(NSString*)html; 59 | 60 | /** 61 | * Creates a data response from an HTML template encoded using UTF-8. 62 | * See -initWithHTMLTemplate:variables: for details. 63 | */ 64 | + (instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; 65 | 66 | /** 67 | * Creates a data response from a serialized JSON object and the default 68 | * "application/json" content type. 69 | */ 70 | + (instancetype)responseWithJSONObject:(id)object; 71 | 72 | /** 73 | * Creates a data response from a serialized JSON object and a custom 74 | * content type. 75 | */ 76 | + (instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type; 77 | 78 | /** 79 | * Initializes a data response from text encoded using UTF-8. 80 | */ 81 | - (instancetype)initWithText:(NSString*)text; 82 | 83 | /** 84 | * Initializes a data response from HTML encoded using UTF-8. 85 | */ 86 | - (instancetype)initWithHTML:(NSString*)html; 87 | 88 | /** 89 | * Initializes a data response from an HTML template encoded using UTF-8. 90 | * 91 | * All occurences of "%variable%" within the HTML template are replaced with 92 | * their corresponding values. 93 | */ 94 | - (instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; 95 | 96 | /** 97 | * Initializes a data response from a serialized JSON object and the default 98 | * "application/json" content type. 99 | */ 100 | - (instancetype)initWithJSONObject:(id)object; 101 | 102 | /** 103 | * Initializes a data response from a serialized JSON object and a custom 104 | * content type. 105 | */ 106 | - (instancetype)initWithJSONObject:(id)object contentType:(NSString*)type; 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Responses/GCDWebServerDataResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @interface GCDWebServerDataResponse () { 35 | @private 36 | NSData* _data; 37 | BOOL _done; 38 | } 39 | @end 40 | 41 | @implementation GCDWebServerDataResponse 42 | 43 | + (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type { 44 | return [[[self class] alloc] initWithData:data contentType:type]; 45 | } 46 | 47 | - (instancetype)initWithData:(NSData*)data contentType:(NSString*)type { 48 | if (data == nil) { 49 | GWS_DNOT_REACHED(); 50 | return nil; 51 | } 52 | 53 | if ((self = [super init])) { 54 | _data = data; 55 | 56 | self.contentType = type; 57 | self.contentLength = data.length; 58 | } 59 | return self; 60 | } 61 | 62 | - (NSData*)readData:(NSError**)error { 63 | NSData* data; 64 | if (_done) { 65 | data = [NSData data]; 66 | } else { 67 | data = _data; 68 | _done = YES; 69 | } 70 | return data; 71 | } 72 | 73 | - (NSString*)description { 74 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 75 | [description appendString:@"\n\n"]; 76 | [description appendString:GCDWebServerDescribeData(_data, self.contentType)]; 77 | return description; 78 | } 79 | 80 | @end 81 | 82 | @implementation GCDWebServerDataResponse (Extensions) 83 | 84 | + (instancetype)responseWithText:(NSString*)text { 85 | return [[self alloc] initWithText:text]; 86 | } 87 | 88 | + (instancetype)responseWithHTML:(NSString*)html { 89 | return [[self alloc] initWithHTML:html]; 90 | } 91 | 92 | + (instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables { 93 | return [[self alloc] initWithHTMLTemplate:path variables:variables]; 94 | } 95 | 96 | + (instancetype)responseWithJSONObject:(id)object { 97 | return [[self alloc] initWithJSONObject:object]; 98 | } 99 | 100 | + (instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type { 101 | return [[self alloc] initWithJSONObject:object contentType:type]; 102 | } 103 | 104 | - (instancetype)initWithText:(NSString*)text { 105 | NSData* data = [text dataUsingEncoding:NSUTF8StringEncoding]; 106 | if (data == nil) { 107 | GWS_DNOT_REACHED(); 108 | return nil; 109 | } 110 | return [self initWithData:data contentType:@"text/plain; charset=utf-8"]; 111 | } 112 | 113 | - (instancetype)initWithHTML:(NSString*)html { 114 | NSData* data = [html dataUsingEncoding:NSUTF8StringEncoding]; 115 | if (data == nil) { 116 | GWS_DNOT_REACHED(); 117 | return nil; 118 | } 119 | return [self initWithData:data contentType:@"text/html; charset=utf-8"]; 120 | } 121 | 122 | - (instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables { 123 | NSMutableString* html = [[NSMutableString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; 124 | [variables enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL* stop) { 125 | [html replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, html.length)]; 126 | }]; 127 | id response = [self initWithHTML:html]; 128 | return response; 129 | } 130 | 131 | - (instancetype)initWithJSONObject:(id)object { 132 | return [self initWithJSONObject:object contentType:@"application/json"]; 133 | } 134 | 135 | - (instancetype)initWithJSONObject:(id)object contentType:(NSString*)type { 136 | NSData* data = [NSJSONSerialization dataWithJSONObject:object options:0 error:NULL]; 137 | if (data == nil) { 138 | return nil; 139 | } 140 | return [self initWithData:data contentType:type]; 141 | } 142 | 143 | @end 144 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Responses/GCDWebServerErrorResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerDataResponse.h" 29 | #import "GCDWebServerHTTPStatusCodes.h" 30 | 31 | /** 32 | * The GCDWebServerDataResponse subclass of GCDWebServerDataResponse generates 33 | * an HTML body from an HTTP status code and an error message. 34 | */ 35 | @interface GCDWebServerErrorResponse : GCDWebServerDataResponse 36 | 37 | /** 38 | * Creates a client error response with the corresponding HTTP status code. 39 | */ 40 | + (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); 41 | 42 | /** 43 | * Creates a server error response with the corresponding HTTP status code. 44 | */ 45 | + (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); 46 | 47 | /** 48 | * Creates a client error response with the corresponding HTTP status code 49 | * and an underlying NSError. 50 | */ 51 | + (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); 52 | 53 | /** 54 | * Creates a server error response with the corresponding HTTP status code 55 | * and an underlying NSError. 56 | */ 57 | + (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); 58 | 59 | /** 60 | * Initializes a client error response with the corresponding HTTP status code. 61 | */ 62 | - (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); 63 | 64 | /** 65 | * Initializes a server error response with the corresponding HTTP status code. 66 | */ 67 | - (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2, 3); 68 | 69 | /** 70 | * Initializes a client error response with the corresponding HTTP status code 71 | * and an underlying NSError. 72 | */ 73 | - (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); 74 | 75 | /** 76 | * Initializes a server error response with the corresponding HTTP status code 77 | * and an underlying NSError. 78 | */ 79 | - (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3, 4); 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Responses/GCDWebServerErrorResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @interface GCDWebServerErrorResponse () 35 | - (instancetype)initWithStatusCode:(NSInteger)statusCode underlyingError:(NSError*)underlyingError messageFormat:(NSString*)format arguments:(va_list)arguments; 36 | @end 37 | 38 | @implementation GCDWebServerErrorResponse 39 | 40 | + (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { 41 | GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); 42 | va_list arguments; 43 | va_start(arguments, format); 44 | GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; 45 | va_end(arguments); 46 | return response; 47 | } 48 | 49 | + (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { 50 | GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); 51 | va_list arguments; 52 | va_start(arguments, format); 53 | GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; 54 | va_end(arguments); 55 | return response; 56 | } 57 | 58 | + (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { 59 | GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); 60 | va_list arguments; 61 | va_start(arguments, format); 62 | GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; 63 | va_end(arguments); 64 | return response; 65 | } 66 | 67 | + (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { 68 | GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); 69 | va_list arguments; 70 | va_start(arguments, format); 71 | GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; 72 | va_end(arguments); 73 | return response; 74 | } 75 | 76 | static inline NSString* _EscapeHTMLString(NSString* string) { 77 | return [string stringByReplacingOccurrencesOfString:@"\"" withString:@"""]; 78 | } 79 | 80 | - (instancetype)initWithStatusCode:(NSInteger)statusCode underlyingError:(NSError*)underlyingError messageFormat:(NSString*)format arguments:(va_list)arguments { 81 | NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments]; 82 | NSString* title = [NSString stringWithFormat:@"HTTP Error %i", (int)statusCode]; 83 | NSString* error = underlyingError ? [NSString stringWithFormat:@"[%@] %@ (%li)", underlyingError.domain, _EscapeHTMLString(underlyingError.localizedDescription), (long)underlyingError.code] : @""; 84 | NSString* html = [NSString stringWithFormat:@"%@

%@: %@

%@

", 85 | title, title, _EscapeHTMLString(message), error]; 86 | if ((self = [self initWithHTML:html])) { 87 | self.statusCode = statusCode; 88 | } 89 | return self; 90 | } 91 | 92 | - (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { 93 | GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); 94 | va_list arguments; 95 | va_start(arguments, format); 96 | self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; 97 | va_end(arguments); 98 | return self; 99 | } 100 | 101 | - (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { 102 | GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); 103 | va_list arguments; 104 | va_start(arguments, format); 105 | self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; 106 | va_end(arguments); 107 | return self; 108 | } 109 | 110 | - (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { 111 | GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); 112 | va_list arguments; 113 | va_start(arguments, format); 114 | self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; 115 | va_end(arguments); 116 | return self; 117 | } 118 | 119 | - (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { 120 | GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); 121 | va_list arguments; 122 | va_start(arguments, format); 123 | self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; 124 | va_end(arguments); 125 | return self; 126 | } 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Responses/GCDWebServerFileResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerResponse.h" 29 | 30 | /** 31 | * The GCDWebServerFileResponse subclass of GCDWebServerResponse reads the body 32 | * of the HTTP response from a file on disk. 33 | * 34 | * It will automatically set the contentType, lastModifiedDate and eTag 35 | * properties of the GCDWebServerResponse according to the file extension and 36 | * metadata. 37 | */ 38 | @interface GCDWebServerFileResponse : GCDWebServerResponse 39 | 40 | /** 41 | * Creates a response with the contents of a file. 42 | */ 43 | + (instancetype)responseWithFile:(NSString*)path; 44 | 45 | /** 46 | * Creates a response like +responseWithFile: and sets the "Content-Disposition" 47 | * HTTP header for a download if the "attachment" argument is YES. 48 | */ 49 | + (instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment; 50 | 51 | /** 52 | * Creates a response like +responseWithFile: but restricts the file contents 53 | * to a specific byte range. 54 | * 55 | * See -initWithFile:byteRange: for details. 56 | */ 57 | + (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range; 58 | 59 | /** 60 | * Creates a response like +responseWithFile:byteRange: and sets the 61 | * "Content-Disposition" HTTP header for a download if the "attachment" 62 | * argument is YES. 63 | */ 64 | + (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment; 65 | 66 | /** 67 | * Initializes a response with the contents of a file. 68 | */ 69 | - (instancetype)initWithFile:(NSString*)path; 70 | 71 | /** 72 | * Initializes a response like +responseWithFile: and sets the 73 | * "Content-Disposition" HTTP header for a download if the "attachment" 74 | * argument is YES. 75 | */ 76 | - (instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment; 77 | 78 | /** 79 | * Initializes a response like -initWithFile: but restricts the file contents 80 | * to a specific byte range. This range should be set to (NSUIntegerMax, 0) for 81 | * the full file, (offset, length) if expressed from the beginning of the file, 82 | * or (NSUIntegerMax, length) if expressed from the end of the file. The "offset" 83 | * and "length" values will be automatically adjusted to be compatible with the 84 | * actual size of the file. 85 | * 86 | * This argument would typically be set to the value of the byteRange property 87 | * of the current GCDWebServerRequest. 88 | */ 89 | - (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range; 90 | 91 | /** 92 | * This method is the designated initializer for the class. 93 | */ 94 | - (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment; 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Responses/GCDWebServerFileResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import 33 | 34 | #import "GCDWebServerPrivate.h" 35 | 36 | #define kFileReadBufferSize (32 * 1024) 37 | 38 | @interface GCDWebServerFileResponse () { 39 | @private 40 | NSString* _path; 41 | NSUInteger _offset; 42 | NSUInteger _size; 43 | int _file; 44 | } 45 | @end 46 | 47 | @implementation GCDWebServerFileResponse 48 | 49 | + (instancetype)responseWithFile:(NSString*)path { 50 | return [[[self class] alloc] initWithFile:path]; 51 | } 52 | 53 | + (instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment { 54 | return [[[self class] alloc] initWithFile:path isAttachment:attachment]; 55 | } 56 | 57 | + (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range { 58 | return [[[self class] alloc] initWithFile:path byteRange:range]; 59 | } 60 | 61 | + (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment { 62 | return [[[self class] alloc] initWithFile:path byteRange:range isAttachment:attachment]; 63 | } 64 | 65 | - (instancetype)initWithFile:(NSString*)path { 66 | return [self initWithFile:path byteRange:NSMakeRange(NSUIntegerMax, 0) isAttachment:NO]; 67 | } 68 | 69 | - (instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment { 70 | return [self initWithFile:path byteRange:NSMakeRange(NSUIntegerMax, 0) isAttachment:attachment]; 71 | } 72 | 73 | - (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range { 74 | return [self initWithFile:path byteRange:range isAttachment:NO]; 75 | } 76 | 77 | static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) { 78 | return [NSDate dateWithTimeIntervalSince1970:((NSTimeInterval)t->tv_sec + (NSTimeInterval)t->tv_nsec / 1000000000.0)]; 79 | } 80 | 81 | - (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment { 82 | struct stat info; 83 | if (lstat([path fileSystemRepresentation], &info) || !(info.st_mode & S_IFREG)) { 84 | GWS_DNOT_REACHED(); 85 | return nil; 86 | } 87 | #ifndef __LP64__ 88 | 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) 89 | GWS_DNOT_REACHED(); 90 | return nil; 91 | } 92 | #endif 93 | NSUInteger fileSize = (NSUInteger)info.st_size; 94 | 95 | BOOL hasByteRange = GCDWebServerIsValidByteRange(range); 96 | if (hasByteRange) { 97 | if (range.location != NSUIntegerMax) { 98 | range.location = MIN(range.location, fileSize); 99 | range.length = MIN(range.length, fileSize - range.location); 100 | } else { 101 | range.length = MIN(range.length, fileSize); 102 | range.location = fileSize - range.length; 103 | } 104 | if (range.length == 0) { 105 | return nil; // TODO: Return 416 status code and "Content-Range: bytes */{file length}" header 106 | } 107 | } else { 108 | range.location = 0; 109 | range.length = fileSize; 110 | } 111 | 112 | if ((self = [super init])) { 113 | _path = [path copy]; 114 | _offset = range.location; 115 | _size = range.length; 116 | if (hasByteRange) { 117 | [self setStatusCode:kGCDWebServerHTTPStatusCode_PartialContent]; 118 | [self setValue:[NSString stringWithFormat:@"bytes %lu-%lu/%lu", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), (unsigned long)fileSize] forAdditionalHeader:@"Content-Range"]; 119 | GWS_LOG_DEBUG(@"Using content bytes range [%lu-%lu] for file \"%@\"", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), path); 120 | } 121 | 122 | if (attachment) { 123 | NSString* fileName = [path lastPathComponent]; 124 | NSData* data = [[fileName stringByReplacingOccurrencesOfString:@"\"" withString:@""] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES]; 125 | NSString* lossyFileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil; 126 | if (lossyFileName) { 127 | NSString* value = [NSString stringWithFormat:@"attachment; filename=\"%@\"; filename*=UTF-8''%@", lossyFileName, GCDWebServerEscapeURLString(fileName)]; 128 | [self setValue:value forAdditionalHeader:@"Content-Disposition"]; 129 | } else { 130 | GWS_DNOT_REACHED(); 131 | } 132 | } 133 | 134 | self.contentType = GCDWebServerGetMimeTypeForExtension([_path pathExtension]); 135 | self.contentLength = _size; 136 | self.lastModifiedDate = _NSDateFromTimeSpec(&info.st_mtimespec); 137 | self.eTag = [NSString stringWithFormat:@"%llu/%li/%li", info.st_ino, info.st_mtimespec.tv_sec, info.st_mtimespec.tv_nsec]; 138 | } 139 | return self; 140 | } 141 | 142 | - (BOOL)open:(NSError**)error { 143 | _file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY); 144 | if (_file <= 0) { 145 | if (error) { 146 | *error = GCDWebServerMakePosixError(errno); 147 | } 148 | return NO; 149 | } 150 | if (lseek(_file, _offset, SEEK_SET) != (off_t)_offset) { 151 | if (error) { 152 | *error = GCDWebServerMakePosixError(errno); 153 | } 154 | close(_file); 155 | return NO; 156 | } 157 | return YES; 158 | } 159 | 160 | - (NSData*)readData:(NSError**)error { 161 | size_t length = MIN((NSUInteger)kFileReadBufferSize, _size); 162 | NSMutableData* data = [[NSMutableData alloc] initWithLength:length]; 163 | ssize_t result = read(_file, data.mutableBytes, length); 164 | if (result < 0) { 165 | if (error) { 166 | *error = GCDWebServerMakePosixError(errno); 167 | } 168 | return nil; 169 | } 170 | if (result > 0) { 171 | [data setLength:result]; 172 | _size -= result; 173 | } 174 | return data; 175 | } 176 | 177 | - (void)close { 178 | close(_file); 179 | } 180 | 181 | - (NSString*)description { 182 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 183 | [description appendFormat:@"\n\n{%@}", _path]; 184 | return description; 185 | } 186 | 187 | @end 188 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Responses/GCDWebServerStreamedResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "GCDWebServerResponse.h" 29 | 30 | /** 31 | * The GCDWebServerStreamBlock is called to stream the data for the HTTP body. 32 | * The block must return either a chunk of data, an empty NSData when done, or 33 | * nil on error and set the "error" argument which is guaranteed to be non-NULL. 34 | */ 35 | typedef NSData* (^GCDWebServerStreamBlock)(NSError** error); 36 | 37 | /** 38 | * The GCDWebServerAsyncStreamBlock works like the GCDWebServerStreamBlock 39 | * except the streamed data can be returned at a later time allowing for 40 | * truly asynchronous generation of the data. 41 | * 42 | * The block must call "completionBlock" passing the new chunk of data when ready, 43 | * an empty NSData when done, or nil on error and pass a NSError. 44 | * 45 | * The block cannot call "completionBlock" more than once per invocation. 46 | */ 47 | typedef void (^GCDWebServerAsyncStreamBlock)(GCDWebServerBodyReaderCompletionBlock completionBlock); 48 | 49 | /** 50 | * The GCDWebServerStreamedResponse subclass of GCDWebServerResponse streams 51 | * the body of the HTTP response using a GCD block. 52 | */ 53 | @interface GCDWebServerStreamedResponse : GCDWebServerResponse 54 | 55 | /** 56 | * Creates a response with streamed data and a given content type. 57 | */ 58 | + (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; 59 | 60 | /** 61 | * Creates a response with async streamed data and a given content type. 62 | */ 63 | + (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block; 64 | 65 | /** 66 | * Initializes a response with streamed data and a given content type. 67 | */ 68 | - (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; 69 | 70 | /** 71 | * This method is the designated initializer for the class. 72 | */ 73 | - (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block; 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /src/ios/GCDWebServer/Responses/GCDWebServerStreamedResponse.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #if !__has_feature(objc_arc) 29 | #error GCDWebServer requires ARC 30 | #endif 31 | 32 | #import "GCDWebServerPrivate.h" 33 | 34 | @interface GCDWebServerStreamedResponse () { 35 | @private 36 | GCDWebServerAsyncStreamBlock _block; 37 | } 38 | @end 39 | 40 | @implementation GCDWebServerStreamedResponse 41 | 42 | + (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block { 43 | return [[[self class] alloc] initWithContentType:type streamBlock:block]; 44 | } 45 | 46 | + (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block { 47 | return [[[self class] alloc] initWithContentType:type asyncStreamBlock:block]; 48 | } 49 | 50 | - (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block { 51 | return [self initWithContentType:type 52 | asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) { 53 | 54 | NSError* error = nil; 55 | NSData* data = block(&error); 56 | completionBlock(data, error); 57 | 58 | }]; 59 | } 60 | 61 | - (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block { 62 | if ((self = [super init])) { 63 | _block = [block copy]; 64 | 65 | self.contentType = type; 66 | } 67 | return self; 68 | } 69 | 70 | - (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block { 71 | _block(block); 72 | } 73 | 74 | - (NSString*)description { 75 | NSMutableString* description = [NSMutableString stringWithString:[super description]]; 76 | [description appendString:@"\n\n"]; 77 | return description; 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /src/ios/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Niklas von Hertzen 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/ios/wk-plugin.js: -------------------------------------------------------------------------------- 1 | 2 | (function _wk_plugin() { 3 | // Check if we are running in WKWebView 4 | if (!window.webkit || !window.webkit.messageHandlers) { 5 | return; 6 | } 7 | 8 | // Initialize Ionic 9 | window.Ionic = window.Ionic || {}; 10 | 11 | function normalizeURL(url) { 12 | if (!url) { 13 | return url; 14 | } 15 | if (!url.startsWith("file://")) { 16 | return url; 17 | } 18 | url = url.substr(7); // len("file://") == 7 19 | if (url.length == 0 || url[0] !== '/') { // ensure the new URL starts with / 20 | url = '/' + url; 21 | } 22 | return 'http://localhost:8080' + url; 23 | } 24 | if (typeof window.wkRewriteURL === 'undefined') { 25 | window.wkRewriteURL = function (url) { 26 | console.warn('wkRewriteURL is deprecated, use normalizeURL instead'); 27 | return normalizeURL(url); 28 | } 29 | } 30 | window.Ionic.normalizeURL = normalizeURL; 31 | 32 | var xhrPrototype = window.XMLHttpRequest.prototype; 33 | var originalOpen = xhrPrototype.open; 34 | 35 | xhrPrototype.open = function _wk_open(method, url) { 36 | arguments[1] = normalizeURL(url); 37 | originalOpen.apply(this, arguments); 38 | } 39 | console.debug("XHR polyfill injected!"); 40 | 41 | var stopScrollHandler = window.webkit.messageHandlers.stopScroll; 42 | if (!stopScrollHandler) { 43 | console.error('Can not find stopScroll handler'); 44 | return; 45 | } 46 | 47 | var stopScrollFunc = null; 48 | var stopScroll = { 49 | stop: function stop(callback) { 50 | if (!stopScrollFunc) { 51 | stopScrollFunc = callback; 52 | stopScrollHandler.postMessage(''); 53 | } 54 | }, 55 | fire: function fire() { 56 | stopScrollFunc && stopScrollFunc(); 57 | stopScrollFunc = null; 58 | }, 59 | cancel: function cancel() { 60 | stopScrollFunc = null; 61 | } 62 | }; 63 | 64 | window.Ionic.StopScroll = stopScroll; 65 | // deprecated 66 | window.IonicStopScroll = stopScroll; 67 | 68 | console.debug("Ionic Stop Scroll injected!"); 69 | })(); 70 | -------------------------------------------------------------------------------- /src/www/ios/ios-wkwebview-exec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one 4 | * or more contributor license agreements. See the NOTICE file 5 | * distributed with this work for additional information 6 | * regarding copyright ownership. The ASF licenses this file 7 | * to you under the Apache License, Version 2.0 (the 8 | * "License"); you may not use this file except in compliance 9 | * with the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, 14 | * software distributed under the License is distributed on an 15 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | * KIND, either express or implied. See the License for the 17 | * specific language governing permissions and limitations 18 | * under the License. 19 | * 20 | */ 21 | 22 | /** 23 | * Creates the exec bridge used to notify the native code of 24 | * commands. 25 | */ 26 | var cordova = require('cordova'); 27 | var utils = require('cordova/utils'); 28 | var base64 = require('cordova/base64'); 29 | 30 | function massageArgsJsToNative (args) { 31 | if (!args || utils.typeName(args) !== 'Array') { 32 | return args; 33 | } 34 | var ret = []; 35 | args.forEach(function (arg, i) { 36 | if (utils.typeName(arg) === 'ArrayBuffer') { 37 | ret.push({ 38 | 'CDVType': 'ArrayBuffer', 39 | 'data': base64.fromArrayBuffer(arg) 40 | }); 41 | } else { 42 | ret.push(arg); 43 | } 44 | }); 45 | return ret; 46 | } 47 | 48 | function massageMessageNativeToJs (message) { 49 | if (message.CDVType === 'ArrayBuffer') { 50 | var stringToArrayBuffer = function (str) { 51 | var ret = new Uint8Array(str.length); 52 | for (var i = 0; i < str.length; i++) { 53 | ret[i] = str.charCodeAt(i); 54 | } 55 | return ret.buffer; 56 | }; 57 | var base64ToArrayBuffer = function (b64) { 58 | return stringToArrayBuffer(atob(b64)); // eslint-disable-line no-undef 59 | }; 60 | message = base64ToArrayBuffer(message.data); 61 | } 62 | return message; 63 | } 64 | 65 | function convertMessageToArgsNativeToJs (message) { 66 | var args = []; 67 | if (!message || !message.hasOwnProperty('CDVType')) { 68 | args.push(message); 69 | } else if (message.CDVType === 'MultiPart') { 70 | message.messages.forEach(function (e) { 71 | args.push(massageMessageNativeToJs(e)); 72 | }); 73 | } else { 74 | args.push(massageMessageNativeToJs(message)); 75 | } 76 | return args; 77 | } 78 | 79 | var iOSExec = function () { 80 | // detect change in bridge, if there is a change, we forward to new bridge 81 | 82 | // if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.cordova && window.webkit.messageHandlers.cordova.postMessage) { 83 | // bridgeMode = jsToNativeModes.WK_WEBVIEW_BINDING; 84 | // } 85 | 86 | var successCallback, failCallback, service, action, actionArgs; 87 | var callbackId = null; 88 | if (typeof arguments[0] !== 'string') { 89 | // FORMAT ONE 90 | successCallback = arguments[0]; 91 | failCallback = arguments[1]; 92 | service = arguments[2]; 93 | action = arguments[3]; 94 | actionArgs = arguments[4]; 95 | 96 | // Since we need to maintain backwards compatibility, we have to pass 97 | // an invalid callbackId even if no callback was provided since plugins 98 | // will be expecting it. The Cordova.exec() implementation allocates 99 | // an invalid callbackId and passes it even if no callbacks were given. 100 | callbackId = 'INVALID'; 101 | } else { 102 | throw new Error('The old format of this exec call has been removed (deprecated since 2.1). Change to: ' + // eslint-disable-line 103 | 'cordova.exec(null, null, \'Service\', \'action\', [ arg1, arg2 ]);'); 104 | } 105 | 106 | // If actionArgs is not provided, default to an empty array 107 | actionArgs = actionArgs || []; 108 | 109 | // Register the callbacks and add the callbackId to the positional 110 | // arguments if given. 111 | if (successCallback || failCallback) { 112 | callbackId = service + cordova.callbackId++; 113 | cordova.callbacks[callbackId] = 114 | {success: successCallback, fail: failCallback}; 115 | } 116 | 117 | actionArgs = massageArgsJsToNative(actionArgs); 118 | 119 | // CB-10133 DataClone DOM Exception 25 guard (fast function remover) 120 | var command = [callbackId, service, action, JSON.parse(JSON.stringify(actionArgs))]; 121 | window.webkit.messageHandlers.cordova.postMessage(command); 122 | }; 123 | 124 | iOSExec.nativeCallback = function (callbackId, status, message, keepCallback, debug) { 125 | var success = status === 0 || status === 1; 126 | var args = convertMessageToArgsNativeToJs(message); 127 | setTimeout(function () { 128 | cordova.callbackFromNative(callbackId, success, status, args, keepCallback); // eslint-disable-line 129 | }, 0); 130 | }; 131 | 132 | // for backwards compatibility 133 | iOSExec.nativeEvalAndFetch = function (func) { 134 | try { 135 | func(); 136 | } catch (e) { 137 | console.log(e); 138 | } 139 | }; 140 | 141 | // Proxy the exec for bridge changes. See CB-10106 142 | 143 | function cordovaExec () { 144 | var cexec = require('cordova/exec'); 145 | var cexec_valid = (typeof cexec.nativeFetchMessages === 'function') && (typeof cexec.nativeEvalAndFetch === 'function') && (typeof cexec.nativeCallback === 'function'); 146 | return (cexec_valid && execProxy !== cexec) ? cexec : iOSExec; 147 | } 148 | 149 | function execProxy () { 150 | cordovaExec().apply(null, arguments); 151 | } 152 | 153 | execProxy.nativeFetchMessages = function () { 154 | return cordovaExec().nativeFetchMessages.apply(null, arguments); 155 | }; 156 | 157 | execProxy.nativeEvalAndFetch = function () { 158 | return cordovaExec().nativeEvalAndFetch.apply(null, arguments); 159 | }; 160 | 161 | execProxy.nativeCallback = function () { 162 | return cordovaExec().nativeCallback.apply(null, arguments); 163 | }; 164 | 165 | module.exports = execProxy; 166 | 167 | if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.cordova && window.webkit.messageHandlers.cordova.postMessage) { 168 | // unregister the old bridge 169 | cordova.define.remove('cordova/exec'); 170 | // redefine bridge to our new bridge 171 | cordova.define('cordova/exec', function (require, exports, module) { 172 | module.exports = execProxy; 173 | }); 174 | } 175 | -------------------------------------------------------------------------------- /tests/ios/CDVWKWebViewEngineTest.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /tests/ios/CDVWKWebViewEngineTest.xcworkspace/xcshareddata/CDVWKWebViewEngineTest.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 6BE9AD73-1B9F-4362-98D7-DC631BEC6185 9 | IDESourceControlProjectName 10 | CDVWKWebViewEngineTest 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | BEF5A5D0FF64801E558286389440357A9233D7DB 14 | https://git-wip-us.apache.org/repos/asf/cordova-plugin-wkwebview-engine.git 15 | 16 | IDESourceControlProjectPath 17 | tests/ios/CDVWKWebViewEngineTest/CDVWKWebViewEngineTest.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | BEF5A5D0FF64801E558286389440357A9233D7DB 21 | ../../../../.. 22 | 23 | IDESourceControlProjectURL 24 | https://git-wip-us.apache.org/repos/asf/cordova-plugin-wkwebview-engine.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | BEF5A5D0FF64801E558286389440357A9233D7DB 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | BEF5A5D0FF64801E558286389440357A9233D7DB 36 | IDESourceControlWCCName 37 | cordova-plugin-wkwebview-engine 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /tests/ios/CDVWKWebViewEngineTest.xcworkspace/xcshareddata/xcschemes/CordovaLib.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /tests/ios/CDVWKWebViewEngineTest/.gitignore: -------------------------------------------------------------------------------- 1 | build -------------------------------------------------------------------------------- /tests/ios/CDVWKWebViewEngineTest/CDVWKWebViewEngineLibTests/CDVWKWebViewEngineTest.m: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | #import 21 | #import 22 | #import "CDVWKWebViewEngine.h" 23 | #import "CDVWKProcessPoolFactory.h" 24 | #import 25 | #import 26 | 27 | @interface CDVWKWebViewEngineTest : XCTestCase 28 | 29 | @property (nonatomic, strong) CDVWKWebViewEngine* plugin; 30 | @property (nonatomic, strong) CDVViewController* viewController; 31 | 32 | @end 33 | 34 | @interface CDVWKWebViewEngine () 35 | 36 | // TODO: expose private interface, if needed 37 | - (BOOL)shouldReloadWebView; 38 | - (BOOL)shouldReloadWebView:(NSURL*)location title:(NSString*)title; 39 | 40 | @end 41 | 42 | @interface CDVViewController () 43 | 44 | // expose property as readwrite, for test purposes 45 | @property (nonatomic, readwrite, strong) NSMutableDictionary* settings; 46 | 47 | @end 48 | 49 | @implementation CDVWKWebViewEngineTest 50 | 51 | - (void)setUp { 52 | [super setUp]; 53 | // Put setup code here. This method is called before the invocation of each test method in the class. 54 | 55 | // NOTE: no app settings are set, so it will rely on default WKWebViewConfiguration settings 56 | self.plugin = [[CDVWKWebViewEngine alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; 57 | self.viewController = [[CDVViewController alloc] init]; 58 | [self.viewController registerPlugin:self.plugin withClassName:NSStringFromClass([self.plugin class])]; 59 | 60 | XCTAssert([self.plugin conformsToProtocol:@protocol(CDVWebViewEngineProtocol)], @"Plugin does not conform to CDVWebViewEngineProtocol"); 61 | } 62 | 63 | - (void)tearDown { 64 | // Put teardown code here. This method is called after the invocation of each test method in the class. 65 | [super tearDown]; 66 | } 67 | 68 | - (void) testCanLoadRequest { 69 | NSURLRequest* fileUrlRequest = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:@"path/to/file.html"]]; 70 | NSURLRequest* httpUrlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://apache.org"]]; 71 | NSURLRequest* miscUrlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"foo://bar"]]; 72 | id webViewEngineProtocol = self.plugin; 73 | 74 | SEL wk_sel = NSSelectorFromString(@"loadFileURL:allowingReadAccessToURL:"); 75 | if ([self.plugin.engineWebView respondsToSelector:wk_sel]) { 76 | XCTAssertTrue([webViewEngineProtocol canLoadRequest:fileUrlRequest]); 77 | } else { 78 | XCTAssertFalse([webViewEngineProtocol canLoadRequest:fileUrlRequest]); 79 | } 80 | 81 | XCTAssertTrue([webViewEngineProtocol canLoadRequest:httpUrlRequest]); 82 | XCTAssertTrue([webViewEngineProtocol canLoadRequest:miscUrlRequest]); 83 | } 84 | 85 | - (void) testUpdateInfo { 86 | // Add -ObjC to Other Linker Flags to test project, to load Categories 87 | // Update objc test template generator as well 88 | 89 | id webViewEngineProtocol = self.plugin; 90 | WKWebView* wkWebView = (WKWebView*)self.plugin.engineWebView; 91 | 92 | // iOS >=10 defaults to NO, < 10 defaults to YES. 93 | BOOL mediaPlaybackRequiresUserActionDefault = IsAtLeastiOSVersion(@"10.0")? NO : YES; 94 | 95 | NSDictionary* preferences = @{ 96 | [@"MinimumFontSize" lowercaseString] : @1.1, // default is 0.0 97 | [@"AllowInlineMediaPlayback" lowercaseString] : @YES, // default is NO 98 | [@"MediaPlaybackRequiresUserAction" lowercaseString] : @(!mediaPlaybackRequiresUserActionDefault), // default is NO on iOS >= 10, YES for < 10 99 | [@"SuppressesIncrementalRendering" lowercaseString] : @YES, // default is NO 100 | [@"MediaPlaybackAllowsAirPlay" lowercaseString] : @NO, // default is YES 101 | [@"DisallowOverscroll" lowercaseString] : @YES, // so bounces is to be NO. defaults to NO 102 | [@"WKWebViewDecelerationSpeed" lowercaseString] : @"fast" // default is 'normal' 103 | }; 104 | NSDictionary* info = @{ 105 | kCDVWebViewEngineWebViewPreferences : preferences 106 | }; 107 | [webViewEngineProtocol updateWithInfo:info]; 108 | 109 | // the only preference we can set, we **can** change this during runtime 110 | XCTAssertEqualWithAccuracy(wkWebView.configuration.preferences.minimumFontSize, 1.1, 0.0001); 111 | 112 | // the WKWebViewConfiguration properties, we **cannot** change outside of initialization 113 | if (IsAtLeastiOSVersion(@"10.0")) { 114 | XCTAssertFalse(wkWebView.configuration.mediaPlaybackRequiresUserAction); 115 | } else { 116 | XCTAssertTrue(wkWebView.configuration.mediaPlaybackRequiresUserAction); 117 | } 118 | XCTAssertFalse(wkWebView.configuration.allowsInlineMediaPlayback); 119 | XCTAssertFalse(wkWebView.configuration.suppressesIncrementalRendering); 120 | XCTAssertTrue(wkWebView.configuration.mediaPlaybackAllowsAirPlay); 121 | 122 | // in the test above, DisallowOverscroll is YES, so no bounce 123 | if ([wkWebView respondsToSelector:@selector(scrollView)]) { 124 | XCTAssertFalse(((UIScrollView*)[wkWebView scrollView]).bounces); 125 | } else { 126 | for (id subview in wkWebView.subviews) { 127 | if ([[subview class] isSubclassOfClass:[UIScrollView class]]) { 128 | XCTAssertFalse(((UIScrollView*)subview).bounces = NO); 129 | } 130 | } 131 | } 132 | 133 | XCTAssertTrue(wkWebView.scrollView.decelerationRate == UIScrollViewDecelerationRateFast); 134 | } 135 | 136 | - (void) testConfigurationFromSettings { 137 | // we need to re-set the plugin from the "setup" to take in the app settings we need 138 | self.plugin = [[CDVWKWebViewEngine alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; 139 | self.viewController = [[CDVViewController alloc] init]; 140 | 141 | // generate the app settings 142 | // iOS >=10 defaults to NO, < 10 defaults to YES. 143 | BOOL mediaPlaybackRequiresUserActionDefault = IsAtLeastiOSVersion(@"10.0")? NO : YES; 144 | 145 | NSDictionary* settings = @{ 146 | [@"MinimumFontSize" lowercaseString] : @1.1, // default is 0.0 147 | [@"AllowInlineMediaPlayback" lowercaseString] : @YES, // default is NO 148 | [@"MediaPlaybackRequiresUserAction" lowercaseString] : @(!mediaPlaybackRequiresUserActionDefault), // default is NO on iOS >= 10, YES for < 10 149 | [@"SuppressesIncrementalRendering" lowercaseString] : @YES, // default is NO 150 | [@"MediaPlaybackAllowsAirPlay" lowercaseString] : @NO, // default is YES 151 | [@"DisallowOverscroll" lowercaseString] : @YES, // so bounces is to be NO. defaults to NO 152 | [@"WKWebViewDecelerationSpeed" lowercaseString] : @"fast" // default is 'normal' 153 | }; 154 | // this can be set because of the Category at the top of the file 155 | self.viewController.settings = [settings mutableCopy]; 156 | 157 | // app settings are read after you register the plugin 158 | [self.viewController registerPlugin:self.plugin withClassName:NSStringFromClass([self.plugin class])]; 159 | XCTAssert([self.plugin conformsToProtocol:@protocol(CDVWebViewEngineProtocol)], @"Plugin does not conform to CDVWebViewEngineProtocol"); 160 | 161 | // after registering (thus plugin initialization), we can grab the webview configuration 162 | WKWebView* wkWebView = (WKWebView*)self.plugin.engineWebView; 163 | 164 | // the only preference we can set, we **can** change this during runtime 165 | XCTAssertEqualWithAccuracy(wkWebView.configuration.preferences.minimumFontSize, 1.1, 0.0001); 166 | 167 | // the WKWebViewConfiguration properties, we **cannot** change outside of initialization 168 | if (IsAtLeastiOSVersion(@"10.0")) { 169 | XCTAssertTrue(wkWebView.configuration.mediaPlaybackRequiresUserAction); 170 | } else { 171 | XCTAssertFalse(wkWebView.configuration.mediaPlaybackRequiresUserAction); 172 | } 173 | XCTAssertTrue(wkWebView.configuration.allowsInlineMediaPlayback); 174 | XCTAssertTrue(wkWebView.configuration.suppressesIncrementalRendering); 175 | // The test case below is in a separate test "testConfigurationWithMediaPlaybackAllowsAirPlay" (Apple bug) 176 | // XCTAssertFalse(wkWebView.configuration.mediaPlaybackAllowsAirPlay); 177 | 178 | // in the test above, DisallowOverscroll is YES, so no bounce 179 | if ([wkWebView respondsToSelector:@selector(scrollView)]) { 180 | XCTAssertFalse(((UIScrollView*)[wkWebView scrollView]).bounces); 181 | } else { 182 | for (id subview in wkWebView.subviews) { 183 | if ([[subview class] isSubclassOfClass:[UIScrollView class]]) { 184 | XCTAssertFalse(((UIScrollView*)subview).bounces = NO); 185 | } 186 | } 187 | } 188 | 189 | XCTAssertTrue(wkWebView.scrollView.decelerationRate == UIScrollViewDecelerationRateFast); 190 | } 191 | 192 | - (void) testShouldReloadWebView { 193 | WKWebView* wkWebView = (WKWebView*)self.plugin.engineWebView; 194 | 195 | NSURL* about_blank = [NSURL URLWithString:@"about:blank"]; 196 | NSURL* real_site = [NSURL URLWithString:@"https://cordova.apache.org"]; 197 | NSString* empty_title_document = @""; 198 | 199 | // about:blank should reload 200 | [wkWebView loadRequest:[NSURLRequest requestWithURL:about_blank]]; 201 | XCTAssertTrue([self.plugin shouldReloadWebView]); 202 | 203 | // a network location should *not* reload 204 | [wkWebView loadRequest:[NSURLRequest requestWithURL:real_site]]; 205 | XCTAssertFalse([self.plugin shouldReloadWebView]); 206 | 207 | // document with empty title should *not* reload 208 | // baseURL:nil results in about:blank, so we use a dummy here 209 | [wkWebView loadHTMLString:empty_title_document baseURL:[NSURL URLWithString:@"about:"]]; 210 | XCTAssertFalse([self.plugin shouldReloadWebView]); 211 | 212 | // Anecdotal assertion that when the WKWebView process has died, 213 | // the title is nil, should always reload 214 | XCTAssertTrue([self.plugin shouldReloadWebView:about_blank title:nil]); 215 | XCTAssertTrue([self.plugin shouldReloadWebView:real_site title:nil]); 216 | 217 | // about:blank should always reload 218 | XCTAssertTrue([self.plugin shouldReloadWebView:about_blank title:@"some title"]); 219 | 220 | // non about:blank with a non-nil title should **not** reload 221 | XCTAssertFalse([self.plugin shouldReloadWebView:real_site title:@""]); 222 | } 223 | 224 | - (void) testConfigurationWithMediaPlaybackAllowsAirPlay { 225 | WKWebViewConfiguration* configuration = [WKWebViewConfiguration new]; 226 | configuration.allowsAirPlayForMediaPlayback = NO; 227 | 228 | WKWebView* wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 100, 100) configuration:configuration]; 229 | 230 | XCTAssertFalse(configuration.allowsAirPlayForMediaPlayback); 231 | // Uh-oh, bug in WKWebView below. Tested on iOS 9, iOS 10 beta 3 232 | XCTAssertFalse(wkWebView.configuration.allowsAirPlayForMediaPlayback); 233 | } 234 | 235 | - (void) testWKProcessPoolFactory { 236 | WKProcessPool* shared = [[CDVWKProcessPoolFactory sharedFactory] sharedProcessPool]; 237 | XCTAssertTrue(shared != nil); 238 | } 239 | 240 | @end 241 | -------------------------------------------------------------------------------- /tests/ios/CDVWKWebViewEngineTest/CDVWKWebViewEngineLibTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/ios/CDVWKWebViewEngineTest/CDVWKWebViewEngineTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /tests/ios/CDVWKWebViewEngineTest/CDVWKWebViewEngineTest.xcodeproj/project.xcworkspace/xcshareddata/CDVWKWebViewEngineTest.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 6BE9AD73-1B9F-4362-98D7-DC631BEC6185 9 | IDESourceControlProjectName 10 | CDVWKWebViewEngineTest 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | BEF5A5D0FF64801E558286389440357A9233D7DB 14 | https://git-wip-us.apache.org/repos/asf/cordova-plugin-wkwebview-engine.git 15 | 16 | IDESourceControlProjectPath 17 | tests/ios/CDVWKWebViewEngineTest/CDVWKWebViewEngineTest.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | BEF5A5D0FF64801E558286389440357A9233D7DB 21 | ../../../../.. 22 | 23 | IDESourceControlProjectURL 24 | https://git-wip-us.apache.org/repos/asf/cordova-plugin-wkwebview-engine.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | BEF5A5D0FF64801E558286389440357A9233D7DB 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | BEF5A5D0FF64801E558286389440357A9233D7DB 36 | IDESourceControlWCCName 37 | cordova-plugin-wkwebview-engine 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /tests/ios/CDVWKWebViewEngineTest/CDVWKWebViewEngineTest.xcodeproj/xcshareddata/xcschemes/CDVWKWebViewEngineLib.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /tests/ios/CDVWKWebViewEngineTest/CDVWKWebViewEngineTest.xcodeproj/xcshareddata/xcschemes/CDVWKWebViewEngineLibTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 44 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 69 | 70 | 76 | 77 | 78 | 79 | 80 | 81 | 87 | 88 | 94 | 95 | 96 | 97 | 99 | 100 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /tests/ios/README.md: -------------------------------------------------------------------------------- 1 | 19 | 20 | # iOS Tests for CDVWKWebViewEngine 21 | 22 | You need to install `node.js` to pull in `cordova-ios`. 23 | 24 | First install cordova-ios: 25 | 26 | npm install 27 | 28 | ... in the current folder. 29 | 30 | 31 | # Testing from Xcode 32 | 33 | 1. Launch the `CDVWKWebViewEngineTest.xcworkspace` file. 34 | 2. Choose "CDVWKWebViewEngineLibTests" from the scheme drop-down menu 35 | 3. Click and hold on the `Play` button, and choose the `Wrench` icon to run the tests 36 | 37 | 38 | # Testing from the command line 39 | 40 | npm test 41 | -------------------------------------------------------------------------------- /tests/ios/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-wkwebview-engine-test-ios", 3 | "version": "1.0.0", 4 | "description": "iOS Unit Tests for cordova-plugin-wkwebview-engine Plugin", 5 | "author": "Apache Software Foundation", 6 | "license": "Apache Version 2.0", 7 | "dependencies": { 8 | "cordova-ios": "*" 9 | }, 10 | "scripts": { 11 | "test": "xcodebuild test -workspace CDVWKWebViewEngineTest.xcworkspace -scheme CDVWKWebViewEngineLibTests -destination 'platform=iOS Simulator,name=iPhone 5' -xcconfig test.xcconfig" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/ios/test.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed to the Apache Software Foundation (ASF) under one 3 | // or more contributor license agreements. See the NOTICE file 4 | // distributed with this work for additional information 5 | // regarding copyright ownership. The ASF licenses this file 6 | // to you under the Apache License, Version 2.0 (the 7 | // "License"); you may not use this file except in compliance 8 | // with the License. You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, 13 | // software distributed under the License is distributed on an 14 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | // KIND, either express or implied. See the License for the 16 | // specific language governing permissions and limitations 17 | // under the License. 18 | // 19 | 20 | HEADER_SEARCH_PATHS = $(TARGET_BUILD_DIR)/include 21 | -------------------------------------------------------------------------------- /tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-wkwebview-engine-tests", 3 | "version": "1.1.3-dev", 4 | "description": "", 5 | "cordova": { 6 | "id": "cordova-plugin-wkwebview-engine-tests", 7 | "platforms": [] 8 | }, 9 | "keywords": [ 10 | "ecosystem:cordova" 11 | ], 12 | "author": "", 13 | "license": "Apache 2.0" 14 | } 15 | -------------------------------------------------------------------------------- /tests/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 24 | cordova-plugin-wkwebview-engine Tests 25 | Apache 2.0 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tests/tests.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one 4 | * or more contributor license agreements. See the NOTICE file 5 | * distributed with this work for additional information 6 | * regarding copyright ownership. The ASF licenses this file 7 | * to you under the Apache License, Version 2.0 (the 8 | * "License"); you may not use this file except in compliance 9 | * with the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, 14 | * software distributed under the License is distributed on an 15 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | * KIND, either express or implied. See the License for the 17 | * specific language governing permissions and limitations 18 | * under the License. 19 | * 20 | */ 21 | 22 | /* jshint jasmine: true */ 23 | 24 | exports.defineAutoTests = function () { 25 | describe('cordova-plugin-wkwebview-engine (cordova)', function () { 26 | it("cordova-plugin-wkwebview-engine.spec.1 should exist", function () { 27 | //expect(window).toBeDefined(); 28 | }); 29 | }); 30 | }; 31 | 32 | exports.defineManualTests = function (contentEl, createActionButton) { 33 | 34 | contentEl.innerHTML = 'Your HTML instructions here'; 35 | 36 | createActionButton('Do something 1', function () { 37 | // do something 1; 38 | }, 'do-something-1'); 39 | 40 | createActionButton('Do something 2', function () { 41 | // do something 2; 42 | }, 'do-something-2'); 43 | 44 | }; 45 | --------------------------------------------------------------------------------