├── .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 |
--------------------------------------------------------------------------------