├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── MIT-LICENSE.txt
├── README.md
├── bower.json
├── package-lock.json
├── package.json
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── github
│ └── jscookie
│ └── javacookie
│ ├── AttributesDefinition.java
│ ├── ConverterException.java
│ ├── ConverterStrategy.java
│ ├── CookieParseException.java
│ ├── CookieSerializationException.java
│ ├── CookieValue.java
│ ├── Cookies.java
│ ├── CookiesDefinition.java
│ └── Expiration.java
└── test
├── java
└── com
│ └── github
│ └── jscookie
│ └── javacookie
│ └── test
│ ├── integration
│ ├── encoding
│ │ ├── CookiesEncodingIT.java
│ │ ├── EncodingPageObject.java
│ │ └── EncodingServlet.java
│ ├── qunit
│ │ ├── QUnitPageObject.java
│ │ └── QUnitResults.java
│ └── test
│ │ └── utils
│ │ └── Debug.java
│ └── unit
│ ├── CookiesConverterTest.java
│ ├── CookiesDecodingTest.java
│ ├── CookiesEncodingTest.java
│ ├── CookiesJSONReadTest.java
│ ├── CookiesJSONWriteTest.java
│ ├── CookiesReadTest.java
│ ├── CookiesWriteTest.java
│ └── utils
│ ├── BaseTest.java
│ └── IntegrationUtils.java
└── resources
├── arquillian.xml
└── web.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /node
3 | /node_modules
4 | /bower_components
5 | /release.properties
6 | /pom.xml.releaseBackup
7 | *.iml
8 | .idea
9 | .vscode
10 | .settings
11 | .classpath
12 | .project
13 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
3 | jdk:
4 | - oraclejdk11
5 | - oraclejdk12
6 | - oraclejdk13
7 | - oraclejdk14
8 | - openjdk8
9 | - openjdk9
10 | - openjdk10
11 | - openjdk11
12 | - openjdk12
13 | - openjdk13
14 | - openjdk14
15 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Issues
2 |
3 | - Report issues or feature requests on [GitHub Issues](https://github.com/js-cookie/java-cookie/issues).
4 | - If reporting a bug, please add a [simplified example](http://sscce.org/).
5 |
6 | ## Pull requests
7 |
8 | - Create a new topic branch for every separate change you make.
9 | - Create a test case if you are fixing a bug or implementing an important feature.
10 | - Make sure the build runs successfully [(see below)](#development).
11 |
12 | ## Development
13 |
14 | ### Tools
15 |
16 | We use the following tools for development:
17 |
18 | - [Maven](https://maven.apache.org/) for Java Build.
19 | - [NodeJS](https://nodejs.org/en/download/) used for NPM (installed by Maven automatically).
20 | - [NPM](https://www.npmjs.com) used to install Bower (installed by Maven automatically).
21 | - [Bower](https://bower.io) used to get [js-cookie](https://github.com/js-cookie/js-cookie/) for Integration tests (installed by NPM automatically).
22 |
23 |
24 | ### Getting started
25 |
26 | Install [Maven](https://maven.apache.org/download.cgi) and add `mvn` as a global alias to run the `/bin/mvn` command inside Maven folder.
27 |
28 | Browse to the project root directory and run the build:
29 |
30 | $ mvn install
31 |
32 | After the build completes, you should see the following message in the console:
33 |
34 | ----------------------------------------------------------------------------
35 | BUILD SUCCESS
36 | ----------------------------------------------------------------------------
37 |
38 | ### Unit tests
39 |
40 | To run the unit tests, execute the following command:
41 |
42 | $ mvn test
43 |
44 | ### Integration tests
45 |
46 | If you want to debug the integration tests in the browser, switch `Debug.FALSE` to `Debug.TRUE` in `CookiesEncodingIT.java` and run the build:
47 |
48 | $ mvn verify
49 |
50 | [Arquillian](http://arquillian.org/) will start the server, [Selenium](http://www.seleniumhq.org/) will run the tests in Firefox, but the build will hang to allow debugging in the browser.
51 |
52 | It uses the [integration hook](https://github.com/js-cookie/js-cookie/blob/master/CONTRIBUTING.md#integration-with-server-side) provided by the project [js-cookie](https://github.com/js-cookie/js-cookie).
53 |
--------------------------------------------------------------------------------
/MIT-LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2014 Fagner Brack
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Java Cookie [](https://travis-ci.org/js-cookie/java-cookie)
2 |
3 | A simple Java API for handling cookies
4 |
5 | * Supports Java 8+, Servlet 2.2+
6 | * [Unobtrusive](#json-data-binding) JSON Data Binding support
7 | * [RFC 6265](http://www.rfc-editor.org/rfc/rfc6265.txt) compliant
8 | * Enable [custom decoding](#converter)
9 |
10 | ## Installation
11 |
12 | Include the maven dependency in your `pom.xml`:
13 |
14 | ```xml
15 |
16 | com.github.js-cookie
17 | java-cookie
18 | 0.0.2
19 |
20 | ```
21 |
22 | If you don't use Maven you can build the artifact from this repository, by installing [git SCM](https://git-scm.com/downloads), [Maven](https://maven.apache.org/download.cgi) and executing the commands below:
23 |
24 | ```shell
25 | $ git clone https://github.com/js-cookie/java-cookie.git
26 | $ cd java-cookie
27 | $ mvn install -P simple
28 | ```
29 |
30 | The artifact will be created inside the `java-cookie/target` folder.
31 |
32 | ## Basic Usage
33 |
34 | Create a cookie, valid across the entire site
35 |
36 | ```java
37 | Cookies cookies = Cookies.initFromServlet( request, response );
38 | cookies.set( "name", "value" );
39 | ```
40 |
41 | Create a cookie that expires 7 days from now, valid across the entire site:
42 |
43 | ```java
44 | Cookies cookies = Cookies.initFromServlet( request, response );
45 | cookies.set( "name", "value", Attributes.empty()
46 | .expires( Expiration.days( 7 ) )
47 | );
48 | ```
49 |
50 | Create an expiring cookie, valid to the path of the current page:
51 |
52 | ```java
53 | Cookies cookies = Cookies.initFromServlet( request, response );
54 | cookies.set( "name", "value", Attributes.empty()
55 | .expires( Expiration.days( 7 ) )
56 | .path( "" )
57 | );
58 | ```
59 |
60 | Read cookie:
61 |
62 | ```java
63 | Cookies cookies = Cookies.initFromServlet( request, response );
64 | cookies.get( "name" ); // => "value"
65 | cookies.get( "nothing" ); // => null
66 | ```
67 |
68 | Read all available cookies:
69 |
70 | ```java
71 | Cookies cookies = Cookies.initFromServlet( request, response );
72 | Map all = cookies.get(); // => {name=value}
73 | ```
74 |
75 | Delete cookie:
76 |
77 | ```java
78 | Cookies cookies = Cookies.initFromServlet( request, response );
79 | cookies.remove( "name" );
80 | ```
81 |
82 | Delete a cookie valid to the path of the current page:
83 |
84 | ```java
85 | Cookies cookies = Cookies.initFromServlet( request, response );
86 | cookies.set( "name", "value", Attributes.empty()
87 | .path( "" )
88 | );
89 | cookies.remove( "name" ); // fail!
90 | cookies.remove( "name", Attributes.empty().path( "path" ) ); // removed!
91 | ```
92 |
93 | *IMPORTANT! when deleting a cookie, you must pass the exact same path, domain and secure attributes that were used to set the cookie, unless you're relying on the [default attributes](#cookie-attributes).*
94 |
95 | ## JSON Data Binding
96 |
97 | java-cookie provides unobtrusive JSON storage for cookies with data binding.
98 |
99 | When creating a cookie, you can pass a few supported types instead of String in the value. If you do so, java-cookie will store the stringified JSON representation of the value using [jackson databind](https://github.com/FasterXML/jackson-databind/#use-it).
100 |
101 | Consider the following class that implements the `CookieValue` interface:
102 |
103 | ```java
104 | public class Person implements CookieValue {
105 | private int age;
106 | public Person( int age ) {
107 | this.age = age;
108 | }
109 | public int getAge() {
110 | return age;
111 | }
112 | }
113 | ```
114 |
115 | And the following usage:
116 |
117 | ```java
118 | Cookies cookies = Cookies.initFromServlet( request, response );
119 | cookies.set( "name", new Person( 25 ) );
120 | ```
121 |
122 | When reading a cookie with the default `get()` api, you receive the string representation stored in the cookie:
123 |
124 | ```java
125 | Cookies cookies = Cookies.initFromServlet( request, response );
126 | String value = cookies.get( "name" ); // => "{\"age\":25}"
127 | ```
128 |
129 | If you pass the type reference, it will parse the JSON into a new instance:
130 |
131 | ```java
132 | Cookies cookies = Cookies.initFromServlet( request, response );
133 | Person adult = cookies.get( "name", Person.class );
134 | if ( adult != null ) {
135 | adult.getAge(); // => 25
136 | }
137 | ```
138 |
139 | ## Encoding
140 |
141 | This project is [RFC 6265](http://tools.ietf.org/html/rfc6265#section-4.1.1) compliant. All special characters that are not allowed in the cookie-name or cookie-value are encoded with each one's UTF-8 Hex equivalent using [percent-encoding](http://en.wikipedia.org/wiki/Percent-encoding).
142 | The only character in cookie-name or cookie-value that is allowed and still encoded is the percent `%` character, it is escaped in order to interpret percent input as literal.
143 | To override the default cookie decoding you need to use a [converter](#converter).
144 |
145 | ## Cookie Attributes
146 |
147 | The default cookie attributes can be set globally by setting properties of the `.defaults()` instance or individually for each call to `.set(...)` by passing an `Attributes` instance in the last argument. Per-call attributes override the default attributes.
148 |
149 | ```java
150 | Cookies cookies = Cookies.initFromServlet( request, response );
151 | cookies.defaults()
152 | .secure( true )
153 | .httpOnly( true );
154 | cookies.set( "name", "value", Attributes.empty()
155 | .httpOnly( false ) // override defaults
156 | );
157 | ```
158 |
159 | ### expires
160 |
161 | Define when the cookie will be removed. Value can be an `Expiration.days()` which will be interpreted as days from time of creation, a `java.util.Date` or an `org.joda.time.DateTime` instance. If omitted, the cookie becomes a session cookie.
162 |
163 | **Default:** Cookie is removed when the user closes the browser.
164 |
165 | **Examples:**
166 |
167 | ```java
168 | DateTime date_2015_06_07_23h38m46s = new DateTime( 2015, 6, 7, 23, 38, 46 );
169 | Cookies cookies = Cookies.initFromServlet( request, response );
170 | cookies.set( "name", "value", Attributes.empty()
171 | .expires( Expiration.date( date_2015_06_07_23h38m46s ) )
172 | );
173 | cookies.get( "name" ); // => "value"
174 | cookies.remove( "name" );
175 | ```
176 |
177 | ### path
178 |
179 | Define the path where the cookie is available.
180 |
181 | **Default:** `/`
182 |
183 | **Examples:**
184 |
185 | ```java
186 | Cookies cookies = Cookies.initFromServlet( request, response );
187 | Attributes validToTheCurrentPage = Attributes.empty().path( "" );
188 | cookies.set( "name", "value", validToTheCurrentPath );
189 | cookies.get( "name" ); // => "value"
190 | cookies.remove( "name", validToTheCurrentPath );
191 | ```
192 |
193 | ### domain
194 |
195 | Define the domain where the cookie is available
196 |
197 | **Default:** Domain of the page where the cookie was created
198 |
199 | **Examples:**
200 |
201 | ```java
202 | Cookies cookies = Cookies.initFromServlet( request, response );
203 | cookies.set( "name", "value", Attributes.empty().domain( "sub.domain.com" ) );
204 | cookies.get( "name" ); // => null (need to read at "sub.domain.com")
205 | ```
206 |
207 | ### secure
208 |
209 | A `Boolean` indicating if the cookie transmission requires a secure protocol (https)
210 |
211 | **Default:** No secure protocol requirement
212 |
213 | **Examples:**
214 |
215 | ```java
216 | Cookies cookies = Cookies.initFromServlet( request, response );
217 | Attributes secureCookie = Attributes.empty().secure( true );
218 | cookies.set( "name", "value", secureCookie );
219 | cookies.get( "name" ); // => "value"
220 | cookies.remove( "name", secureCookie );
221 | ```
222 |
223 | ### httpOnly
224 |
225 | A `Boolean` indicating if the cookie should be restricted to be manipulated only in the server.
226 |
227 | **Default:** The cookie can be manipulated in the server and in the client
228 |
229 | **Examples:**
230 |
231 | ```java
232 | Cookies cookies = Cookies.initFromServlet( request, response );
233 | Attributes httpOnlyCookie = Attributes.empty().httpOnly( true );
234 | cookies.set( "name", "value", httpOnlyCookie );
235 | cookies.get( "name" ); // => "value"
236 | cookies.remove( "name", httpOnlyCookie );
237 | ```
238 |
239 | ### sameSite
240 |
241 | Define whether your cookie should be restricted to a first party or same-site context
242 |
243 | **Default:** not set
244 |
245 | Note that more recent browsers are making "Lax" the default value even without specifying anything here.
246 |
247 | **Examples:**
248 |
249 | ```java
250 | Cookies cookies = Cookies.initFromServlet( request, response );
251 | cookies.set( "name", "value", Attributes.empty().sameSite( "Lax" ) );
252 | cookies.get( "name" ); // => "value"
253 | ```
254 |
255 | ## Converter
256 |
257 | Create a new instance of the api that overrides the default decoding implementation.
258 | All methods that rely in a proper decoding to work, such as `remove()` and `get()`, will run the converter first for each cookie.
259 | The returning String will be used as the cookie value.
260 |
261 | Example from reading one of the cookies that can only be decoded using the Javascript `escape` function:
262 |
263 | ``` java
264 | // document.cookie = 'escaped=%u5317';
265 | // document.cookie = 'default=%E5%8C%97';
266 |
267 | Cookies cookies = Cookies.initFromServlet( request, response );
268 | Cookies escapedCookies = cookies.withConverter(new Cookies.Converter() {
269 | @Override
270 | public String convert( String value, String name ) throws ConverterException {
271 | ScriptEngine javascript = new ScriptEngineManager().getEngineByName( "JavaScript" );
272 | if ( name.equals( "escaped" ) ) {
273 | try {
274 | return javascript.eval( "unescape('" + value + "')" ).toString();
275 | } catch ( ScriptException e ) {
276 | throw new ConverterException( e );
277 | }
278 | }
279 | return null;
280 | }
281 | });
282 |
283 | escapedCookies.get( "escaped" ); // => 北
284 | escapedCookies.get( "default" ); // => 北
285 | escapedCookies.get(); // => {escaped=北, default=北}
286 | ```
287 |
288 | Instead of passing a converter inline, you can also create a custom strategy by implementing the `ConverterStrategy` interface:
289 |
290 | ```java
291 | class CustomConverter implements ConverterStrategy {
292 | @Override
293 | public String convert( String value, String name ) throws ConverterException {
294 | return value;
295 | }
296 | }
297 | ```
298 |
299 | ```java
300 | Cookies cookies = Cookies.initFromServlet( request, response );
301 | Cookies cookiesWithCustomConverter = cookies.withConverter( new CustomConverter() );
302 | ```
303 |
304 | ## Contributing
305 |
306 | Check out the [Contributing Guidelines](CONTRIBUTING.md).
307 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "java-cookie",
3 | "devDependencies": {
4 | "js-cookie": "2.0.3"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "java-cookie",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "bower": {
8 | "version": "1.8.8",
9 | "resolved": "https://registry.npmjs.org/bower/-/bower-1.8.8.tgz",
10 | "integrity": "sha512-1SrJnXnkP9soITHptSO+ahx3QKp3cVzn8poI6ujqc5SeOkg5iqM1pK9H+DSc2OQ8SnO0jC/NG4Ur/UIwy7574A==",
11 | "dev": true
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "java-cookie",
3 | "version": "1.0.0",
4 | "license": "MIT",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/js-cookie/java-cookie.git"
8 | },
9 | "devDependencies": {
10 | "bower": "1.8.8"
11 | },
12 | "scripts": {
13 | "test": "cd bower_components/js-cookie && ../../node/node \"../../node/node_modules/npm/bin/npm-cli.js\" install"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | com.github.js-cookie
5 | java-cookie
6 | 0.1.0
7 | Java Cookie
8 | A simple Servlet API for handling cookies
9 | https://github.com/js-cookie/java-cookie
10 |
11 | UTF-8
12 | UTF-8
13 | 2.45.0
14 | 20.0.1.Final
15 | java-cookie-scm
16 | 1.8
17 |
18 |
19 |
20 | The MIT License (MIT)
21 | http://opensource.org/licenses/MIT
22 | repo
23 |
24 |
25 |
26 |
27 | Fagner Brack
28 | github@fagnermartins.com
29 | js-cookie
30 | https://github.com/js-cookie
31 |
32 |
33 |
34 | https://github.com/js-cookie/java-cookie
35 | scm:git:https://github.com/js-cookie/java-cookie.git
36 | scm:git:https://github.com/js-cookie/java-cookie.git
37 | v0.1.0
38 |
39 |
40 |
41 | RedHat GA
42 | https://maven.repository.redhat.com/ga/
43 |
44 |
45 |
46 |
47 |
48 | maven-compiler-plugin
49 | 3.8.1
50 |
51 | ${java.version}
52 | ${java.version}
53 |
54 |
55 |
56 | org.apache.maven.plugins
57 | maven-release-plugin
58 | 3.0.0-M1
59 |
60 | false
61 | release
62 | v@{project.version}
63 | deploy
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | org.jboss.arquillian
72 | arquillian-bom
73 | 1.6.0.Final
74 | test
75 | pom
76 |
77 |
78 |
79 |
80 |
81 | javax.servlet
82 | servlet-api
83 | 2.2
84 | provided
85 |
86 |
87 | joda-time
88 | joda-time
89 | 2.10.6
90 |
91 |
92 | com.fasterxml.jackson.core
93 | jackson-databind
94 | 2.11.2
95 |
96 |
97 | junit
98 | junit
99 | 4.13.1
100 | test
101 |
102 |
103 | org.mockito
104 | mockito-all
105 | 2.0.2-beta
106 | test
107 |
108 |
109 | org.seleniumhq.selenium
110 | selenium-java
111 | ${selenium.version}
112 | test
113 |
114 |
115 | org.seleniumhq.selenium
116 | selenium-firefox-driver
117 | ${selenium.version}
118 | test
119 |
120 |
121 | org.jboss.arquillian.protocol
122 | arquillian-protocol-servlet
123 | 1.6.0.Final
124 | test
125 |
126 |
127 | org.jboss.arquillian.junit
128 | arquillian-junit-container
129 | 1.6.0.Final
130 | test
131 |
132 |
133 | org.wildfly.arquillian
134 | wildfly-arquillian-container-managed
135 | 2.2.0.Final
136 | test
137 |
138 |
139 | org.jboss.shrinkwrap.resolver
140 | shrinkwrap-resolver-impl-maven
141 | 3.1.4
142 | test
143 |
144 |
145 | org.jboss.shrinkwrap.resolver
146 | shrinkwrap-resolver-spi
147 | 3.1.4
148 | test
149 |
150 |
151 | org.apache.httpcomponents
152 | httpclient
153 | 4.5.13
154 | test
155 |
156 |
157 | org.apache.httpcomponents
158 | fluent-hc
159 | 4.5.12
160 | test
161 |
162 |
163 |
164 |
165 | test
166 |
167 | true
168 |
169 |
170 |
171 |
172 | maven-dependency-plugin
173 |
174 |
175 | unpack
176 | process-test-classes
177 |
178 | unpack
179 |
180 |
181 |
182 |
183 | org.wildfly
184 | wildfly-dist
185 | ${wildfly.version}
186 | zip
187 | false
188 | target
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 | org.apache.maven.plugins
197 | maven-failsafe-plugin
198 | 2.22.2
199 |
200 |
201 |
202 | integration-test
203 | verify
204 |
205 |
206 |
207 |
208 |
209 | com.github.eirslett
210 | frontend-maven-plugin
211 | 1.10.0
212 |
213 |
214 | install node and npm
215 |
216 | install-node-and-npm
217 |
218 |
219 | v12.18.2
220 |
221 |
222 |
223 |
224 | npm install
225 |
226 | npm
227 |
228 |
229 |
230 | bower install
231 |
232 | bower
233 |
234 |
235 |
236 | npm test
237 |
238 | npm
239 |
240 |
241 | test
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 | release
251 |
252 |
253 | ossrh
254 | https://oss.sonatype.org/content/repositories/snapshots
255 |
256 |
257 | ossrh
258 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
259 |
260 |
261 |
262 |
263 |
264 | org.apache.maven.plugins
265 | maven-javadoc-plugin
266 | 3.2.0
267 |
268 |
269 | attach-javadocs
270 |
271 | jar
272 |
273 |
274 |
275 |
276 |
277 | org.apache.maven.plugins
278 | maven-source-plugin
279 | 3.2.1
280 |
281 |
282 | attach-sources
283 |
284 | jar-no-fork
285 |
286 |
287 |
288 |
289 |
290 | org.sonatype.plugins
291 | nexus-staging-maven-plugin
292 | 1.6.8
293 | true
294 |
295 | ossrh
296 | https://oss.sonatype.org/
297 | true
298 |
299 |
300 |
301 | org.apache.maven.plugins
302 | maven-gpg-plugin
303 | 1.5
304 |
305 |
306 | sign-artifacts
307 | verify
308 |
309 | sign
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 | simple
319 |
320 |
321 |
322 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jscookie/javacookie/AttributesDefinition.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie;
2 |
3 | public abstract class AttributesDefinition {
4 | public abstract AttributesDefinition expires( Expiration expiration );
5 | abstract Expiration expires();
6 | public abstract AttributesDefinition path( String path );
7 | abstract String path();
8 | public abstract AttributesDefinition domain( String domain );
9 | abstract String domain();
10 | public abstract AttributesDefinition secure( Boolean secure );
11 | abstract Boolean secure();
12 | public abstract AttributesDefinition httpOnly( Boolean httpOnly );
13 | abstract Boolean httpOnly();
14 | public abstract AttributesDefinition sameSite( String sameSite );
15 | abstract String sameSite();
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jscookie/javacookie/ConverterException.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie;
2 |
3 | public final class ConverterException extends Exception {
4 | private static final long serialVersionUID = 1;
5 | public ConverterException( Throwable cause ) {
6 | super( cause );
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jscookie/javacookie/ConverterStrategy.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie;
2 |
3 | public interface ConverterStrategy {
4 | /**
5 | * Apply the decoding strategy of a cookie. The return will be used as the cookie value
6 | *
7 | * @return null
if the default encoding mechanism should be used instead
8 | */
9 | public abstract String convert( String value, String name ) throws ConverterException;
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jscookie/javacookie/CookieParseException.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie;
2 |
3 | public final class CookieParseException extends Exception {
4 | private static final long serialVersionUID = 1;
5 | @SuppressWarnings( "unused" )
6 | private CookieParseException() {}
7 | CookieParseException( Throwable cause ) {
8 | super( cause );
9 | }
10 | CookieParseException( String msg ) {
11 | super( msg );
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jscookie/javacookie/CookieSerializationException.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie;
2 |
3 | public class CookieSerializationException extends Exception {
4 | private static final long serialVersionUID = 1;
5 | @SuppressWarnings( "unused" )
6 | private CookieSerializationException() {}
7 | CookieSerializationException( Throwable cause ) {
8 | super( cause );
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jscookie/javacookie/CookieValue.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie;
2 |
3 | /**
4 | * Use this interface to mark that a given class can be used as a cookie value and
5 | * serialized/deserialized to/from JSON
6 | */
7 | public interface CookieValue {}
8 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jscookie/javacookie/Cookies.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie;
2 |
3 | import java.io.CharArrayWriter;
4 | import java.io.IOException;
5 | import java.io.UnsupportedEncodingException;
6 | import java.util.HashMap;
7 | import java.util.HashSet;
8 | import java.util.List;
9 | import java.util.Map;
10 | import java.util.ResourceBundle;
11 | import java.util.Set;
12 | import java.util.regex.Matcher;
13 | import java.util.regex.Pattern;
14 |
15 | import javax.servlet.http.HttpServletRequest;
16 | import javax.servlet.http.HttpServletResponse;
17 |
18 | import com.fasterxml.jackson.core.JsonProcessingException;
19 | import com.fasterxml.jackson.core.type.TypeReference;
20 | import com.fasterxml.jackson.databind.ObjectMapper;
21 |
22 | public final class Cookies implements CookiesDefinition {
23 | private static String UTF_8 = "UTF-8";
24 | private HttpServletRequest request;
25 | private HttpServletResponse response;
26 | private AttributesDefinition defaults = Attributes.empty();
27 | private ConverterStrategy converter;
28 | private ObjectMapper mapper = new ObjectMapper();
29 |
30 | private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
31 | private static ResourceBundle lStrings = ResourceBundle.getBundle( LSTRING_FILE );
32 |
33 | private Cookies( HttpServletRequest request, HttpServletResponse response, ConverterStrategy converter ) {
34 | this.request = request;
35 | this.response = response;
36 | this.converter = converter;
37 | }
38 |
39 | public static Cookies initFromServlet( HttpServletRequest request, HttpServletResponse response ) {
40 | return new Cookies( request, response, null );
41 | }
42 |
43 | @Override
44 | public synchronized String get( String name ) {
45 | if ( name == null || name.length() == 0 ) {
46 | throw new IllegalArgumentException( lStrings.getString( "err.cookie_name_blank" ) );
47 | }
48 |
49 | String cookieHeader = request.getHeader( "cookie" );
50 | if ( cookieHeader == null ) {
51 | return null;
52 | }
53 |
54 | Map cookies = getCookies( cookieHeader );
55 | for ( String decodedName : cookies.keySet() ) {
56 | if ( !name.equals( decodedName ) ) {
57 | continue;
58 | }
59 | return cookies.get( decodedName );
60 | }
61 |
62 | return null;
63 | }
64 |
65 | @Override
66 | public T get( String name, Class dataType ) throws CookieParseException {
67 | String value = get( name );
68 | if ( value == null ) {
69 | return null;
70 | }
71 | try {
72 | return mapper.readValue( value, dataType );
73 | } catch ( IOException e ) {
74 | throw new CookieParseException( e );
75 | }
76 | }
77 |
78 | @Override
79 | public T get( String name, TypeReference typeRef ) throws CookieParseException {
80 | String value = get( name );
81 | if ( value == null ) {
82 | return null;
83 | }
84 | try {
85 | return mapper.readValue( value, typeRef );
86 | } catch ( IOException e ) {
87 | throw new CookieParseException( e );
88 | }
89 | }
90 |
91 | @Override
92 | public Map get() {
93 | Map result = new HashMap();
94 |
95 | String cookieHeader = request.getHeader( "cookie" );
96 | if ( cookieHeader == null ) {
97 | return result;
98 | }
99 |
100 | return getCookies( cookieHeader );
101 | }
102 |
103 | @Override
104 | public synchronized void set( String name, String value, AttributesDefinition attributes ) {
105 | if ( name == null || name.length() == 0 ) {
106 | throw new IllegalArgumentException( lStrings.getString( "err.cookie_name_blank" ) );
107 | }
108 | if ( value == null ) {
109 | throw new IllegalArgumentException();
110 | }
111 | if ( attributes == null ) {
112 | throw new IllegalArgumentException();
113 | }
114 |
115 | String encodedName = encode( name );
116 | String encodedValue = encodeValue( value );
117 |
118 | StringBuilder header = new StringBuilder();
119 | header.append( encodedName );
120 | header.append( '=' );
121 | header.append( encodedValue );
122 |
123 | attributes = extend( Attributes.empty().path( "/" ), defaults, attributes );
124 |
125 | String path = attributes.path();
126 | if ( path != null && !path.isEmpty() ) {
127 | header.append( "; Path=" + path );
128 | }
129 |
130 | Expiration expires = attributes.expires();
131 | if ( expires != null ) {
132 | header.append( "; Expires=" + expires.toExpiresString() );
133 | }
134 |
135 | String domain = attributes.domain();
136 | if ( domain != null ) {
137 | header.append( "; Domain=" + domain );
138 | }
139 |
140 | Boolean secure = attributes.secure();
141 | if ( Boolean.TRUE.equals( secure ) ) {
142 | header.append( "; Secure" );
143 | }
144 |
145 | Boolean httpOnly = attributes.httpOnly();
146 | if ( Boolean.TRUE.equals( httpOnly ) ) {
147 | header.append( "; HttpOnly" );
148 | }
149 |
150 | String sameSite = attributes.sameSite();
151 | if ( sameSite != null ) {
152 | header.append( "; SameSite=" + sameSite );
153 | }
154 |
155 | if ( response.isCommitted() ) {
156 | return;
157 | }
158 |
159 | setCookie( header.toString(), response );
160 | }
161 |
162 | @Override
163 | public void set( String name, int value, AttributesDefinition attributes ) throws CookieSerializationException {
164 | set( name, String.valueOf( value ), attributes );
165 | }
166 |
167 | @Override
168 | public void set( String name, boolean value, AttributesDefinition attributes ) throws CookieSerializationException {
169 | set( name, String.valueOf( value ), attributes );
170 | }
171 |
172 | @Override
173 | public void set( String name, List value, AttributesDefinition attributes ) throws CookieSerializationException {
174 | try {
175 | set( name, mapper.writeValueAsString( value ), attributes );
176 | } catch ( JsonProcessingException e ) {
177 | throw new CookieSerializationException( e );
178 | }
179 | }
180 |
181 | @Override
182 | public void set( String name, CookieValue value, AttributesDefinition attributes ) throws CookieSerializationException {
183 | try {
184 | set( name, mapper.writeValueAsString( value ), attributes );
185 | } catch ( JsonProcessingException e ) {
186 | throw new CookieSerializationException( e );
187 | }
188 | }
189 |
190 | @Override
191 | public void set( String name, String value ) {
192 | if ( name == null || name.length() == 0 ) {
193 | throw new IllegalArgumentException( lStrings.getString( "err.cookie_name_blank" ) );
194 | }
195 | if ( value == null ) {
196 | throw new IllegalArgumentException();
197 | }
198 | set( name, value, defaults );
199 | }
200 |
201 | @Override
202 | public void set( String name, int value ) throws CookieSerializationException {
203 | set( name, value, Attributes.empty() );
204 | }
205 |
206 | @Override
207 | public void set( String name, boolean value ) {
208 | set( name, String.valueOf( value ) );
209 | }
210 |
211 | @Override
212 | public void set( String name, List value ) throws CookieSerializationException {
213 | set( name, value, Attributes.empty() );
214 | }
215 |
216 | @Override
217 | public void set( String name, CookieValue value ) throws CookieSerializationException {
218 | set( name, value, Attributes.empty() );
219 | }
220 |
221 | @Override
222 | public void remove( String name, AttributesDefinition attributes ) {
223 | if ( name == null || name.length() == 0 ) {
224 | throw new IllegalArgumentException( lStrings.getString( "err.cookie_name_blank" ) );
225 | }
226 | if ( attributes == null ) {
227 | throw new IllegalArgumentException();
228 | }
229 |
230 | set( name, "", extend( attributes, Attributes.empty()
231 | .expires( Expiration.days( -1 ) ))
232 | );
233 | }
234 |
235 | @Override
236 | public void remove( String name ) {
237 | if ( name == null || name.length() == 0 ) {
238 | throw new IllegalArgumentException( lStrings.getString( "err.cookie_name_blank" ) );
239 | }
240 | remove( name, Attributes.empty() );
241 | }
242 |
243 | @Override
244 | public AttributesDefinition defaults() {
245 | return this.defaults;
246 | }
247 |
248 | @Override
249 | public Cookies withConverter( ConverterStrategy converter ) {
250 | return new Cookies( request, response, converter );
251 | }
252 |
253 | private Attributes extend( AttributesDefinition... mergeables ) {
254 | Attributes result = Attributes.empty();
255 | for ( AttributesDefinition mergeable : mergeables ) {
256 | result.merge( mergeable );
257 | }
258 | return result;
259 | }
260 |
261 | private void setCookie( String cookieValue, HttpServletResponse response ) {
262 | response.addHeader( "Set-Cookie", cookieValue );
263 | }
264 |
265 | private String encode( String decoded ) {
266 | return encode( decoded, new HashSet() );
267 | }
268 |
269 | private String encode( String decoded, Set exceptions ) {
270 | String encoded = decoded;
271 | for ( int i = 0; i < decoded.length(); ) {
272 | int codePoint = decoded.codePointAt( i );
273 | i += Character.charCount( codePoint );
274 |
275 | boolean isDigit = codePoint >= codePoint( "0" ) && codePoint <= codePoint( "9" );
276 | if ( isDigit ) {
277 | continue;
278 | }
279 |
280 | boolean isAsciiUppercaseLetter = codePoint >= codePoint( "A" ) && codePoint <= codePoint( "Z" );
281 | if ( isAsciiUppercaseLetter ) {
282 | continue;
283 | }
284 |
285 | boolean isAsciiLowercaseLetter = codePoint >= codePoint( "a" ) && codePoint <= codePoint( "z" );
286 | if ( isAsciiLowercaseLetter ) {
287 | continue;
288 | }
289 |
290 | boolean isAllowed =
291 | codePoint == codePoint( "!" ) || codePoint == codePoint( "#" ) ||
292 | codePoint == codePoint( "$" ) || codePoint == codePoint( "&" ) ||
293 | codePoint == codePoint( "'" ) || codePoint == codePoint( "*" ) ||
294 | codePoint == codePoint( "+" ) || codePoint == codePoint( "-" ) ||
295 | codePoint == codePoint( "." ) || codePoint == codePoint( "^" ) ||
296 | codePoint == codePoint( "_" ) || codePoint == codePoint( "`" ) ||
297 | codePoint == codePoint( "|" ) || codePoint == codePoint( "~" );
298 | if ( isAllowed ) {
299 | continue;
300 | }
301 |
302 | if ( exceptions.contains( codePoint ) ) {
303 | continue;
304 | }
305 |
306 | try {
307 | String character = new String( Character.toChars( codePoint ) );
308 | CharArrayWriter hexSequence = new CharArrayWriter();
309 | byte[] bytes = character.getBytes( UTF_8 );
310 | for ( int bytesIndex = 0; bytesIndex < bytes.length; bytesIndex++ ) {
311 | char left = Character.forDigit( bytes[ bytesIndex ] >> 4 & 0xF, 16 );
312 | char right = Character.forDigit( bytes[ bytesIndex ] & 0xF, 16 );
313 | hexSequence
314 | .append( '%' )
315 | .append( left )
316 | .append( right );
317 | }
318 | String target = character.toString();
319 | String sequence = hexSequence.toString().toUpperCase();
320 | encoded = encoded.replace( target, sequence );
321 | } catch ( UnsupportedEncodingException e ) {
322 | e.printStackTrace();
323 | }
324 | }
325 | return encoded;
326 | }
327 |
328 | private String decode( String encoded ) {
329 | String decoded = encoded;
330 | Pattern pattern = Pattern.compile( "(%[0-9A-Z]{2})+" );
331 | Matcher matcher = pattern.matcher( encoded );
332 | while ( matcher.find() ) {
333 | String encodedChar = matcher.group();
334 | String[] encodedBytes = encodedChar.split( "%" );
335 | byte[] bytes = new byte[ encodedBytes.length - 1 ];
336 | for ( int i = 1; i < encodedBytes.length; i++ ) {
337 | String encodedByte = encodedBytes[ i ];
338 | bytes[ i - 1 ] = ( byte )Integer.parseInt( encodedByte, 16 );
339 | }
340 | try {
341 | String decodedChar = new String( bytes, UTF_8 );
342 | decoded = decoded.replace( encodedChar, decodedChar );
343 | } catch ( UnsupportedEncodingException e ) {
344 | e.printStackTrace();
345 | }
346 | }
347 | return decoded;
348 | }
349 |
350 | private String encodeValue( String decodedValue ) {
351 | Set exceptions = new HashSet();
352 | for ( int i = 0; i < decodedValue.length(); ) {
353 | int codePoint = decodedValue.codePointAt( i );
354 | i += Character.charCount( codePoint );
355 |
356 | boolean isIgnorable = false;
357 | if ( codePoint == codePoint( "/" ) || codePoint == codePoint( ":") ) {
358 | isIgnorable = true;
359 | }
360 |
361 | if ( codePoint >= codePoint( "<" ) && codePoint <= codePoint( "@" ) ) {
362 | isIgnorable = true;
363 | }
364 |
365 | if ( codePoint == codePoint( "[" ) || codePoint == codePoint( "]" ) ) {
366 | isIgnorable = true;
367 | }
368 |
369 | if ( codePoint == codePoint( "{" ) || codePoint == codePoint( "}" ) ) {
370 | isIgnorable = true;
371 | }
372 |
373 | if ( isIgnorable ) {
374 | exceptions.add( codePoint );
375 | }
376 | }
377 |
378 | return encode( decodedValue, exceptions );
379 | }
380 |
381 | private int codePoint( String character ) {
382 | return character.codePointAt( 0 );
383 | }
384 |
385 | private String decodeValue( String encodedValue, String decodedName ) {
386 | String decodedValue = null;
387 |
388 | if ( converter != null ) {
389 | try {
390 | decodedValue = converter.convert( encodedValue, decodedName );
391 | } catch ( ConverterException e ) {
392 | e.printStackTrace();
393 | }
394 | }
395 |
396 | if ( decodedValue == null ) {
397 | decodedValue = decode( encodedValue );
398 | }
399 |
400 | return decodedValue;
401 | }
402 |
403 | private Map getCookies( String cookieHeader ) {
404 | Map result = new HashMap();
405 | String[] cookies = cookieHeader.split( "; " );
406 | for ( int i = 0; i < cookies.length; i++ ) {
407 | String cookie = cookies[ i ];
408 | String encodedName = cookie.split( "=" )[ 0 ];
409 | String decodedName = decode( encodedName );
410 |
411 | String encodedValue = cookie.substring( cookie.indexOf( '=' ) + 1, cookie.length() );
412 | String decodedValue = decodeValue( encodedValue, decodedName );
413 | result.put( decodedName, decodedValue );
414 | }
415 | return result;
416 | }
417 |
418 | public static class Attributes extends AttributesDefinition {
419 | private Expiration expires;
420 | private String path;
421 | private String domain;
422 | private Boolean secure;
423 | private Boolean httpOnly;
424 | private String sameSite;
425 |
426 | private Attributes() {}
427 |
428 | public static Attributes empty() {
429 | return new Attributes();
430 | }
431 |
432 | @Override
433 | Expiration expires() {
434 | return expires;
435 | }
436 | @Override
437 | public Attributes expires( Expiration expires ) {
438 | this.expires = expires;
439 | return this;
440 | }
441 |
442 | @Override
443 | String path() {
444 | return path;
445 | }
446 | @Override
447 | public Attributes path( String path ) {
448 | this.path = path;
449 | return this;
450 | }
451 |
452 | @Override
453 | String domain() {
454 | return domain;
455 | }
456 | @Override
457 | public Attributes domain( String domain ) {
458 | this.domain = domain;
459 | return this;
460 | }
461 |
462 | @Override
463 | Boolean secure() {
464 | return secure;
465 | }
466 | @Override
467 | public Attributes secure( Boolean secure ) {
468 | this.secure = secure;
469 | return this;
470 | }
471 |
472 | @Override
473 | Boolean httpOnly() {
474 | return httpOnly;
475 | }
476 | @Override
477 | public Attributes httpOnly( Boolean httpOnly ) {
478 | this.httpOnly = httpOnly;
479 | return this;
480 | }
481 |
482 | @Override
483 | String sameSite() {
484 | return sameSite;
485 | }
486 | @Override
487 | public Attributes sameSite( String sameSite ) {
488 | this.sameSite = sameSite;
489 | return this;
490 | }
491 |
492 | private Attributes merge( AttributesDefinition reference ) {
493 | if ( reference.path() != null ) {
494 | path = reference.path();
495 | }
496 | if ( reference.domain() != null ) {
497 | domain = reference.domain();
498 | }
499 | if ( reference.expires() != null ) {
500 | expires = reference.expires();
501 | }
502 | if ( reference.secure() != null ) {
503 | secure = reference.secure();
504 | }
505 | if ( reference.httpOnly() != null ) {
506 | httpOnly = reference.httpOnly();
507 | }
508 | if ( reference.sameSite() != null ) {
509 | sameSite = reference.sameSite();
510 | }
511 | return this;
512 | }
513 | }
514 |
515 | public static abstract class Converter implements ConverterStrategy {}
516 | }
517 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jscookie/javacookie/CookiesDefinition.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 |
6 | import com.fasterxml.jackson.core.type.TypeReference;
7 |
8 | interface CookiesDefinition {
9 | /**
10 | * Retrieves a cookie
11 | * By default, assumes the characters not allowed in each cookie name are encoded with each
12 | * one's UTF-8 Hex equivalent using percent-encoding.
13 | *
14 | * @return null
if the cookie doesn't exist
15 | */
16 | String get( String name );
17 |
18 | /**
19 | * Retrieves a cookie and parse it using the given dataType instance
20 | *
21 | * @throws CookieParseException
22 | * If there's an error while parsing the cookie name using the given dataType
23 | */
24 | T get( String name, Class dataType ) throws CookieParseException;
25 |
26 | /**
27 | * Retrieves a cookie and parse it using the given type reference. The type reference is used
28 | * to infer generic types from within a class and workaround Java Type Erasure.
29 | *
30 | * For more information check http://wiki.fasterxml.com/JacksonDataBinding#Full_Data_Binding
31 | *
32 | * @throws CookieParseException
33 | * If there's an error while parsing the cookie name using the given dataType
34 | *
35 | * @see #get(String, Class)
36 | */
37 | T get( String name, TypeReference typeRef ) throws CookieParseException;
38 |
39 | /**
40 | * Retrieves all cookies
41 | *
42 | * @see #get(String)
43 | */
44 | Map get();
45 |
46 | /**
47 | * Create or update an existing cookie extending the default attributes
48 | * By default, the characters not allowed in the cookie name or value are encoded with each
49 | * one's UTF-8 Hex equivalent using percent-encoding.
50 | *
51 | * @see #get(String)
52 | */
53 | void set( String name, String value, AttributesDefinition attributes );
54 |
55 | /**
56 | * Create or update an existing cookie extending the default attributes and serializing the typed value
57 | *
58 | * @see #set(String, String, AttributesDefinition)
59 | */
60 | void set( String name, int value, AttributesDefinition attributes ) throws CookieSerializationException;
61 |
62 | /**
63 | * Create or update an existing cookie extending the default attributes and serializing the typed value
64 | *
65 | * @see #set(String, String, AttributesDefinition)
66 | */
67 | void set( String name, boolean value, AttributesDefinition attributes ) throws CookieSerializationException;
68 |
69 | /**
70 | * Create or update an existing cookie extending the default attributes and serializing the typed value
71 | *
72 | * @see #set(String, String, AttributesDefinition)
73 | */
74 | void set( String name, List value, AttributesDefinition attributes ) throws CookieSerializationException;
75 |
76 | /**
77 | * Create or update an existing cookie extending the default attributes and serializing the typed value
78 | *
79 | * @see #set(String, String, AttributesDefinition)
80 | */
81 | void set( String name, CookieValue value, AttributesDefinition attributes ) throws CookieSerializationException;
82 |
83 | /**
84 | * Create or update an existing cookie using the default attributes
85 | *
86 | * @see #set(String, String, AttributesDefinition)
87 | */
88 | void set( String name, String value );
89 |
90 | /**
91 | * Create or update an existing cookie using the default attributes and serializing the typed value
92 | *
93 | * @see #set(String, String)
94 | */
95 | void set( String name, int value ) throws CookieSerializationException;
96 |
97 | /**
98 | * Create or update an existing cookie using the default attributes and serializing the typed value
99 | *
100 | * @see #set(String, String)
101 | */
102 | void set( String name, boolean value ) throws CookieSerializationException;
103 |
104 | /**
105 | * Create or update an existing cookie using the default attributes and serializing the typed value
106 | *
107 | * @see #set(String, String)
108 | */
109 | void set( String name, List value ) throws CookieSerializationException;
110 |
111 | /**
112 | * Create or update an existing cookie extending the default attributes and serializing the typed value
113 | *
114 | * @see #set(String, String)
115 | */
116 | void set( String name, CookieValue value ) throws CookieSerializationException;
117 |
118 | /**
119 | * Remove an existing cookie
120 | * By default, assumes the characters not allowed in each cookie name are encoded with each
121 | * one's UTF-8 Hex equivalent using percent-encoding.
122 | *
123 | * @param attributes
124 | * You must pass the exact same path, domain and secure attributes that were used to set
125 | * the cookie, unless you're relying on the default attributes
126 | *
127 | * @see #get(String)
128 | */
129 | void remove( String name, AttributesDefinition attributes );
130 |
131 | /**
132 | * Remove an existing cookie using the default attributes
133 | *
134 | * @see #remove(String, AttributesDefinition)
135 | */
136 | void remove( String name );
137 |
138 | /**
139 | * Retrieve the default attributes of this instance
140 | */
141 | AttributesDefinition defaults();
142 |
143 | /**
144 | * Create a new instance of the api that overrides the default decoding implementation
145 | * All methods that rely in a proper decoding to work, such as
146 | * {@link #remove(String, AttributesDefinition)} and {@link #get(String)}, will run the converter first
147 | * for each cookie.
148 | * The returning String will be used as the cookie value.
149 | */
150 | CookiesDefinition withConverter( ConverterStrategy converter );
151 | }
152 |
--------------------------------------------------------------------------------
/src/main/java/com/github/jscookie/javacookie/Expiration.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie;
2 |
3 | import java.util.Date;
4 | import java.util.Locale;
5 |
6 | import org.joda.time.DateTime;
7 | import org.joda.time.format.DateTimeFormat;
8 | import org.joda.time.format.DateTimeFormatter;
9 |
10 | public final class Expiration {
11 | private DateTimeFormatter EXPIRES_FORMAT = DateTimeFormat
12 | .forPattern( "EEE, dd MMM yyyy HH:mm:ss 'GMT'" )
13 | .withLocale( Locale.US );
14 | private final DateTime date;
15 | private Expiration( DateTime dateTime ) {
16 | this.date = dateTime;
17 | }
18 | public static Expiration days( int days ) {
19 | DateTime withDays = new DateTime().plusDays( days );
20 | return new Expiration( withDays );
21 | }
22 | public static Expiration date( DateTime dateTime ) {
23 | if ( dateTime == null ) {
24 | throw new IllegalArgumentException();
25 | }
26 | return new Expiration( dateTime );
27 | }
28 | public static Expiration date( Date date ) {
29 | if ( date == null ) {
30 | throw new IllegalArgumentException();
31 | }
32 | DateTime dateTime = new DateTime( date );
33 | return new Expiration( dateTime );
34 | }
35 | String toExpiresString() {
36 | return date.toString( EXPIRES_FORMAT );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/integration/encoding/CookiesEncodingIT.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.integration.encoding;
2 |
3 | import java.io.File;
4 | import java.net.URL;
5 | import java.util.concurrent.TimeUnit;
6 |
7 | import org.jboss.arquillian.container.test.api.Deployment;
8 | import org.jboss.arquillian.container.test.api.RunAsClient;
9 | import org.jboss.arquillian.junit.Arquillian;
10 | import org.jboss.arquillian.test.api.ArquillianResource;
11 | import org.jboss.shrinkwrap.api.Archive;
12 | import org.jboss.shrinkwrap.api.Filters;
13 | import org.jboss.shrinkwrap.api.GenericArchive;
14 | import org.jboss.shrinkwrap.api.ShrinkWrap;
15 | import org.jboss.shrinkwrap.api.importer.ExplodedImporter;
16 | import org.jboss.shrinkwrap.api.spec.WebArchive;
17 | import org.junit.Assert;
18 | import org.junit.Test;
19 | import org.junit.runner.RunWith;
20 | import org.openqa.selenium.WebDriver;
21 | import org.openqa.selenium.firefox.FirefoxDriver;
22 | import org.openqa.selenium.support.PageFactory;
23 |
24 | import com.github.jscookie.javacookie.test.integration.qunit.QUnitPageObject;
25 | import com.github.jscookie.javacookie.test.integration.qunit.QUnitResults;
26 | import com.github.jscookie.javacookie.test.integration.test.utils.Debug;
27 | import com.github.jscookie.javacookie.test.unit.utils.IntegrationUtils;
28 |
29 | @RunWith( Arquillian.class )
30 | public class CookiesEncodingIT {
31 | private static Debug debug = Debug.FALSE;
32 |
33 | @Deployment
34 | public static Archive> createDeployment() {
35 | GenericArchive qunitFiles = ShrinkWrap.create( GenericArchive.class )
36 | .as( ExplodedImporter.class )
37 | .importDirectory( "bower_components/js-cookie/" )
38 | .as( GenericArchive.class );
39 |
40 | WebArchive war = IntegrationUtils.createCommonDeployment()
41 | .merge( qunitFiles, "/", Filters.includeAll() )
42 | .addAsWebInfResource(
43 | new File( "src/test/resources/web.xml" ),
44 | "web.xml"
45 | );
46 |
47 | System.out.println( " ----- LOGGING THE FILES ADDED TO JBOSS" );
48 | System.out.println( war.toString( true ) );
49 | System.out.println( " ----- END OF LOGGING THE FILES ADDED TO JBOSS" );
50 |
51 | return war;
52 | }
53 |
54 | @RunAsClient
55 | @Test
56 | public void read_qunit_test( @ArquillianResource URL baseURL ) {
57 | WebDriver driver = new FirefoxDriver();
58 | driver.manage().timeouts().implicitlyWait( 20, TimeUnit.SECONDS );
59 |
60 | EncodingPageObject encoding = PageFactory.initElements( driver, EncodingPageObject.class );
61 | encoding.navigateTo( baseURL );
62 | QUnitPageObject qunit = encoding.qunit();
63 | QUnitResults results = qunit.waitForTests( debug );
64 |
65 | int expectedPasses = results.getTotal();
66 | int actualPasses = results.getPassed();
67 | Assert.assertEquals( "should pass all tests", expectedPasses, actualPasses );
68 |
69 | if ( debug.is( false ) ) {
70 | driver.quit();
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/integration/encoding/EncodingPageObject.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.integration.encoding;
2 |
3 | import java.net.URL;
4 |
5 | import org.openqa.selenium.WebDriver;
6 | import org.openqa.selenium.support.PageFactory;
7 |
8 | import com.github.jscookie.javacookie.test.integration.qunit.QUnitPageObject;
9 |
10 | public class EncodingPageObject {
11 | private WebDriver driver;
12 | public EncodingPageObject( WebDriver driver ) {
13 | this.driver = driver;
14 | }
15 | EncodingPageObject navigateTo( URL baseURL ) {
16 | String query = "integration_baseurl=" + baseURL;
17 | String url = baseURL + "test/encoding.html";
18 | driver.navigate().to( url + "?" + query );
19 | return this;
20 | }
21 | QUnitPageObject qunit() {
22 | return PageFactory.initElements( driver, QUnitPageObject.class );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/integration/encoding/EncodingServlet.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.integration.encoding;
2 |
3 | import java.io.IOException;
4 | import java.io.UnsupportedEncodingException;
5 | import java.net.URLDecoder;
6 |
7 | import javax.servlet.ServletException;
8 | import javax.servlet.http.HttpServlet;
9 | import javax.servlet.http.HttpServletRequest;
10 | import javax.servlet.http.HttpServletResponse;
11 |
12 | import com.fasterxml.jackson.databind.ObjectMapper;
13 | import com.github.jscookie.javacookie.Cookies;
14 |
15 | public class EncodingServlet extends HttpServlet {
16 | private static final long serialVersionUID = 1;
17 | @Override
18 | public void doGet( HttpServletRequest request, HttpServletResponse response )
19 | throws ServletException, IOException {
20 | String name = getUTF8Param( "name", request );
21 |
22 | System.out.println( "Testing: " + name );
23 |
24 | Cookies cookies = Cookies.initFromServlet( request, response );
25 | String value = cookies.get( name );
26 |
27 | if ( value == null ) {
28 | throw new NullPointerException( "Cookie not found with name: " + name );
29 | }
30 |
31 | cookies.set( name, value );
32 |
33 | response.setContentType( "application/json" );
34 | new ObjectMapper()
35 | .writeValue( response.getOutputStream(), new Result( name, value ) );
36 | }
37 |
38 | /**
39 | * Retrieves the parameter using UTF-8 charset since the server default is ISO-8859-1
40 | */
41 | private String getUTF8Param( String name, HttpServletRequest request ) throws UnsupportedEncodingException {
42 | String query = request.getQueryString();
43 | for ( String pair : query.split( "&" ) ) {
44 | if ( name.equals( pair.split( "=" )[ 0 ] ) ) {
45 | return URLDecoder.decode( pair.split( "=" )[ 1 ], "UTF-8" );
46 | }
47 | }
48 | return null;
49 | }
50 | }
51 |
52 | class Result {
53 | private String name;
54 | private String value;
55 | Result( String name, String value ) {
56 | this.name = name;
57 | this.value = value;
58 | }
59 | public String getName() {
60 | return name;
61 | }
62 | public String getValue() {
63 | return value;
64 | }
65 | }
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/integration/qunit/QUnitPageObject.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.integration.qunit;
2 |
3 | import java.io.IOException;
4 |
5 | import org.openqa.selenium.JavascriptExecutor;
6 | import org.openqa.selenium.WebDriver;
7 | import org.openqa.selenium.support.ui.WebDriverWait;
8 |
9 | import com.fasterxml.jackson.databind.ObjectMapper;
10 | import com.github.jscookie.javacookie.test.integration.test.utils.Debug;
11 | import com.google.common.base.Function;
12 |
13 | public class QUnitPageObject {
14 | private WebDriver driver;
15 | private static int TIMEOUT_IN_SECONDS = 1800;
16 | private ObjectMapper mapper = new ObjectMapper();
17 | public QUnitPageObject( WebDriver driver ) {
18 | this.driver = driver;
19 | }
20 | public QUnitResults waitForTests( final Debug debug ) {
21 | return new WebDriverWait( driver, TIMEOUT_IN_SECONDS )
22 | .until(new Function() {
23 | @Override
24 | public QUnitResults apply( WebDriver input ) {
25 | JavascriptExecutor js;
26 | if ( driver instanceof JavascriptExecutor ) {
27 | js = ( JavascriptExecutor )driver;
28 | String result = ( String )js.executeScript(
29 | "return window.global_test_results && JSON.stringify(window.global_test_results)"
30 | );
31 | if ( debug.is( true ) ) {
32 | return null;
33 | }
34 | System.out.println( "Waiting for 'window.global_test_results': " + result );
35 | if ( result == null ) {
36 | return null;
37 | }
38 | try {
39 | return mapper.readValue( result, QUnitResults.class );
40 | } catch ( IOException cause ) {
41 | throw new GlobalTestResultException( result, cause );
42 | }
43 | }
44 | return null;
45 | }
46 | });
47 | }
48 | private class GlobalTestResultException extends IllegalStateException {
49 | private static final long serialVersionUID = 1;
50 | private GlobalTestResultException( String result, Throwable cause ) {
51 | super( "Failed to read 'window.global_test_results': " + result, cause );
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/integration/qunit/QUnitResults.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.integration.qunit;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public class QUnitResults {
7 | private int failed;
8 | private int passed;
9 | private int runtime;
10 | private int total;
11 | private List tests = new ArrayList();
12 |
13 | public int getFailed() {
14 | return failed;
15 | }
16 |
17 | public int getPassed() {
18 | return passed;
19 | }
20 |
21 | public int getRuntime() {
22 | return runtime;
23 | }
24 |
25 | public List getTests() {
26 | return tests;
27 | }
28 |
29 | public int getTotal() {
30 | return total;
31 | }
32 |
33 | public static class Test {
34 | private String actual = "";
35 | private String expected = "";
36 | private String name = "";
37 | private boolean result = false;
38 | private String source;
39 |
40 | public String getActual() {
41 | return actual;
42 | }
43 |
44 | public String getExpected() {
45 | return expected;
46 | }
47 |
48 | public String getName() {
49 | return name;
50 | }
51 |
52 | public boolean getResult() {
53 | return result;
54 | }
55 |
56 | public String getSource() {
57 | return source;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/integration/test/utils/Debug.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.integration.test.utils;
2 |
3 | public enum Debug {
4 | TRUE( true ), FALSE( false );
5 | private boolean state;
6 | private Debug( boolean state ) {
7 | this.state = state;
8 | }
9 | public boolean is( boolean state ) {
10 | return this.state == state;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/unit/CookiesConverterTest.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.unit;
2 |
3 | import javax.script.ScriptEngine;
4 | import javax.script.ScriptEngineManager;
5 | import javax.script.ScriptException;
6 |
7 | import org.junit.Assert;
8 | import org.junit.Before;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.mockito.Mockito;
12 | import org.mockito.runners.MockitoJUnitRunner;
13 |
14 | import com.github.jscookie.javacookie.ConverterException;
15 | import com.github.jscookie.javacookie.ConverterStrategy;
16 | import com.github.jscookie.javacookie.Cookies;
17 | import com.github.jscookie.javacookie.test.unit.utils.BaseTest;
18 |
19 | @RunWith( MockitoJUnitRunner.class )
20 | public class CookiesConverterTest extends BaseTest {
21 | private Cookies cookies;
22 |
23 | @Before
24 | public void before() {
25 | cookies = Cookies.initFromServlet( request, response );
26 | }
27 |
28 | @Test
29 | public void should_be_able_to_conditionally_decode_a_single_malformed_cookie() {
30 | Mockito.when( request.getHeader( "cookie" ) ).thenReturn(
31 | "escaped=%u5317; encoded=%E4%BA%AC"
32 | );
33 | Cookies cookies = this.cookies.withConverter(new Cookies.Converter() {
34 | @Override
35 | public String convert( String value, String name ) throws ConverterException {
36 | ScriptEngine javascript = new ScriptEngineManager().getEngineByName( "JavaScript" );
37 | if ( name.equals( "escaped" ) ) {
38 | try {
39 | return javascript.eval( "unescape('" + value + "')" ).toString();
40 | } catch ( ScriptException e ) {
41 | throw new ConverterException( e );
42 | }
43 | }
44 | return null;
45 | }
46 | });
47 |
48 | String actual = cookies.get( "escaped" );
49 | String expected = "北";
50 | Assert.assertEquals( expected, actual );
51 |
52 | actual = cookies.get( "encoded" );
53 | expected = "京";
54 | Assert.assertEquals( expected, actual );
55 | }
56 |
57 | @Test
58 | public void should_be_able_to_create_a_custom_strategy() {
59 | this.cookies.withConverter( new CustomConverter() );
60 | }
61 |
62 | private class CustomConverter implements ConverterStrategy {
63 | @Override
64 | public String convert( String value, String name ) throws ConverterException {
65 | return value;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/unit/CookiesDecodingTest.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.unit;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.mockito.Mockito;
8 | import org.mockito.runners.MockitoJUnitRunner;
9 |
10 | import com.github.jscookie.javacookie.Cookies;
11 | import com.github.jscookie.javacookie.test.unit.utils.BaseTest;
12 |
13 | @RunWith( MockitoJUnitRunner.class )
14 | public class CookiesDecodingTest extends BaseTest {
15 | private Cookies cookies;
16 |
17 | @Before
18 | public void before() {
19 | cookies = Cookies.initFromServlet( request, response );
20 | }
21 |
22 | @Test
23 | public void character_not_allowed_in_name_and_value() {
24 | Mockito.when( request.getHeader( "cookie" ) ).thenReturn( "%3B=%3B" );
25 | String actual = cookies.get( ";" );
26 | String expected = ";";
27 | Assert.assertEquals( expected, actual );
28 | }
29 |
30 | @Test
31 | public void character_with_3_bytes() {
32 | Mockito.when( request.getHeader( "cookie" ) ).thenReturn( "c=%E4%BA%AC" );
33 | String actual = cookies.get( "c" );
34 | String expected = "京";
35 | Assert.assertEquals( expected, actual );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/unit/CookiesEncodingTest.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.unit;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 | import org.mockito.Mockito;
7 | import org.mockito.runners.MockitoJUnitRunner;
8 |
9 | import com.github.jscookie.javacookie.Cookies;
10 | import com.github.jscookie.javacookie.test.unit.utils.BaseTest;
11 |
12 | @RunWith( MockitoJUnitRunner.class )
13 | public class CookiesEncodingTest extends BaseTest {
14 | private Cookies cookies;
15 |
16 | @Before
17 | public void before() {
18 | cookies = Cookies.initFromServlet( request, response );
19 | }
20 |
21 | @Test
22 | public void character_not_allowed_in_name_and_value() {
23 | cookies.set( ";,\\\" ", ";,\\\" " );
24 | Mockito.verify( response ).addHeader( "Set-Cookie", "%3B%2C%5C%22%20=%3B%2C%5C%22%20; Path=/" );
25 | }
26 |
27 | @Test
28 | public void characters_allowed_in_name_and_value() {
29 | cookies.set(
30 | "!#$&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~",
31 | "!#$&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~"
32 | );
33 | Mockito.verify( response ).addHeader(
34 | "Set-Cookie",
35 | "!#$&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~=!#$&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~; Path=/"
36 | );
37 | }
38 |
39 | @Test
40 | public void character_with_3_bytes_in_value() {
41 | cookies.set( "c", "京" );
42 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=%E4%BA%AC; Path=/" );
43 | }
44 |
45 | @Test
46 | public void character_with_3_bytes_in_name() {
47 | cookies.set( "京", "v" );
48 | Mockito.verify( response ).addHeader( "Set-Cookie", "%E4%BA%AC=v; Path=/" );
49 | }
50 |
51 | @Test
52 | public void character_with_4_bytes_in_name() {
53 | cookies.set( "c", "𩸽" );
54 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=%F0%A9%B8%BD; Path=/" );
55 | }
56 |
57 | @Test
58 | public void character_with_4_bytes_mixed_with_single_bytes() {
59 | cookies.set( "c", "a𩸽b" );
60 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=a%F0%A9%B8%BDb; Path=/" );
61 | }
62 |
63 | @Test
64 | public void characters_allowed_in_cookie_value() {
65 | cookies.set( "c", "/:<=>?@[]{}" );
66 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=/:<=>?@[]{}; Path=/" );
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/unit/CookiesJSONReadTest.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.unit;
2 |
3 | import java.util.List;
4 |
5 | import org.junit.Assert;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 | import org.mockito.Mockito;
10 | import org.mockito.runners.MockitoJUnitRunner;
11 |
12 | import com.fasterxml.jackson.annotation.JsonProperty;
13 | import com.fasterxml.jackson.core.type.TypeReference;
14 | import com.github.jscookie.javacookie.CookieParseException;
15 | import com.github.jscookie.javacookie.Cookies;
16 | import com.github.jscookie.javacookie.test.unit.utils.BaseTest;
17 |
18 | @RunWith( MockitoJUnitRunner.class )
19 | public class CookiesJSONReadTest extends BaseTest {
20 | private Cookies cookies;
21 |
22 | @Before
23 | public void before() {
24 | cookies = Cookies.initFromServlet( request, response );
25 | }
26 |
27 | @Test
28 | public void read_int_type() throws CookieParseException {
29 | Mockito.when( request.getHeader( "cookie" ) ).thenReturn( "c=1" );
30 |
31 | String actual = cookies.get( "c" );
32 | String expected = "1";
33 | Assert.assertEquals( expected, actual );
34 |
35 | int actual2 = cookies.get( "c", Integer.class );
36 | int expected2 = 1;
37 | Assert.assertEquals( expected2, actual2 );
38 | }
39 |
40 | @Test
41 | public void read_boolean_type() throws CookieParseException {
42 | Mockito.when( request.getHeader( "cookie" ) ).thenReturn( "c=true" );
43 |
44 | String actual = cookies.get( "c" );
45 | String expected = "true";
46 | Assert.assertEquals( expected, actual );
47 |
48 | boolean actual2 = cookies.get( "c", Boolean.class );
49 | boolean expected2 = true;
50 | Assert.assertEquals( expected2, actual2 );
51 | }
52 |
53 | @Test
54 | public void read_JSON_array_with_string() throws CookieParseException {
55 | Mockito.when( request.getHeader( "cookie" ) ).thenReturn( "c=[%22v%22]" );
56 |
57 | String actual = cookies.get( "c" );
58 | String expected = "[\"v\"]";
59 | Assert.assertEquals( expected, actual );
60 |
61 | String actual2 = cookies.get( "c", new TypeReference>() {} ).get( 0 );
62 | String expected2 = "v";
63 | Assert.assertEquals( expected2, actual2 );
64 | }
65 |
66 | @Test
67 | public void read_custom_type_with_string_prop() throws CookieParseException {
68 | Mockito.when( request.getHeader( "cookie" ) ).thenReturn( "c={%22property%22:%22v%22}" );
69 |
70 | String actual = cookies.get( "c" );
71 | String expected = "{\"property\":\"v\"}";
72 | Assert.assertEquals( expected, actual );
73 |
74 | String actual2 = cookies.get( "c", CustomTypeString.class ).getProperty();
75 | String expected2 = "v";
76 | Assert.assertEquals( expected2, actual2 );
77 | }
78 |
79 | @Test
80 | public void read_custom_type_with_boolean_prop() throws CookieParseException {
81 | Mockito.when( request.getHeader( "cookie" ) ).thenReturn( "c={%22property%22:true}" );
82 |
83 | String actual = cookies.get( "c" );
84 | String expected = "{\"property\":true}";
85 | Assert.assertEquals( expected, actual );
86 |
87 | Boolean actual2 = cookies.get( "c", CustomTypeBoolean.class ).getProperty();
88 | Boolean expected2 = true;
89 | Assert.assertEquals( expected2, actual2 );
90 | }
91 |
92 | @Test
93 | public void read_custom_type_with_number_prop() throws CookieParseException {
94 | Mockito.when( request.getHeader( "cookie" ) ).thenReturn( "c={%22property%22:1}" );
95 |
96 | String actual = cookies.get( "c" );
97 | String expected = "{\"property\":1}";
98 | Assert.assertEquals( expected, actual );
99 |
100 | Integer actual2 = cookies.get( "c", CustomTypeInteger.class ).getProperty();
101 | Integer expected2 = 1;
102 | Assert.assertEquals( expected2, actual2 );
103 | }
104 |
105 | @Test
106 | public void read_missing_cookie() throws CookieParseException {
107 | Mockito.when( request.getHeader( "cookie" ) ).thenReturn( null );
108 |
109 | Assert.assertNull( cookies.get( "c" ) );
110 | Assert.assertNull( cookies.get( "c", CustomTypeInteger.class ) );
111 | Assert.assertNull( cookies.get( "c", CustomTypeBoolean.class ) );
112 | Assert.assertNull( cookies.get( "c", CustomTypeString.class ) );
113 | Assert.assertNull( cookies.get( "c", new TypeReference>() {} ) );
114 | }
115 |
116 | private static class CustomTypeString {
117 | private String property;
118 | @JsonProperty( "property" )
119 | private String getProperty() {
120 | return property;
121 | }
122 | }
123 |
124 | private static class CustomTypeBoolean {
125 | private Boolean property;
126 | @JsonProperty( "property" )
127 | private Boolean getProperty() {
128 | return property;
129 | }
130 | }
131 |
132 | private static class CustomTypeInteger {
133 | private Integer property;
134 | @JsonProperty( "property" )
135 | private Integer getProperty() {
136 | return property;
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/unit/CookiesJSONWriteTest.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.unit;
2 |
3 | import java.util.Arrays;
4 |
5 | import org.junit.Before;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 | import org.mockito.Mockito;
9 | import org.mockito.runners.MockitoJUnitRunner;
10 |
11 | import com.github.jscookie.javacookie.CookieSerializationException;
12 | import com.github.jscookie.javacookie.CookieValue;
13 | import com.github.jscookie.javacookie.Cookies;
14 | import com.github.jscookie.javacookie.test.unit.utils.BaseTest;
15 |
16 | @RunWith( MockitoJUnitRunner.class )
17 | public class CookiesJSONWriteTest extends BaseTest {
18 | private Cookies cookies;
19 |
20 | @Before
21 | public void before() {
22 | cookies = Cookies.initFromServlet( request, response );
23 | }
24 |
25 | @Test
26 | public void write_int_type() throws CookieSerializationException {
27 | cookies.set( "c", 1 );
28 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=1; Path=/" );
29 | }
30 |
31 | @Test
32 | public void write_boolean_type() throws CookieSerializationException {
33 | cookies.set( "c", true );
34 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=true; Path=/" );
35 | }
36 |
37 | @Test
38 | public void write_JSON_array_with_string() throws CookieSerializationException {
39 | cookies.set( "c", Arrays.asList( "v" ) );
40 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=[%22v%22]; Path=/" );
41 | }
42 |
43 | @Test
44 | public void write_custom_type_with_string_prop() throws CookieSerializationException {
45 | cookies.set( "c", new CustomTypeString( "v" ) );
46 | Mockito.verify( response ).addHeader( "Set-Cookie", "c={%22property%22:%22v%22}; Path=/" );
47 | }
48 |
49 | @Test
50 | public void write_custom_type_with_boolean_prop() throws CookieSerializationException {
51 | cookies.set( "c", new CustomTypeBoolean( true ) );
52 | Mockito.verify( response ).addHeader( "Set-Cookie", "c={%22property%22:true}; Path=/" );
53 | }
54 |
55 | @Test
56 | public void write_custom_type_with_number_prop() throws CookieSerializationException {
57 | cookies.set( "c", new CustomTypeInteger( 1 ) );
58 | Mockito.verify( response ).addHeader( "Set-Cookie", "c={%22property%22:1}; Path=/" );
59 | }
60 |
61 | class CustomTypeString implements CookieValue {
62 | private String property;
63 | private CustomTypeString( String property ) {
64 | this.property = property;
65 | }
66 | public String getProperty() {
67 | return property;
68 | }
69 | }
70 |
71 | class CustomTypeBoolean implements CookieValue {
72 | private Boolean property;
73 | private CustomTypeBoolean( Boolean property ) {
74 | this.property = property;
75 | }
76 | public Boolean getProperty() {
77 | return property;
78 | }
79 | }
80 |
81 | class CustomTypeInteger implements CookieValue {
82 | private Integer property;
83 | private CustomTypeInteger( Integer property ) {
84 | this.property = property;
85 | }
86 | public Integer getProperty() {
87 | return property;
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/unit/CookiesReadTest.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.unit;
2 |
3 | import java.util.Map;
4 |
5 | import org.junit.Assert;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 | import org.mockito.Mockito;
10 | import org.mockito.runners.MockitoJUnitRunner;
11 |
12 | import com.github.jscookie.javacookie.Cookies;
13 | import com.github.jscookie.javacookie.test.unit.utils.BaseTest;
14 |
15 | @RunWith( MockitoJUnitRunner.class )
16 | public class CookiesReadTest extends BaseTest {
17 | private Cookies cookies;
18 |
19 | @Before
20 | public void before() {
21 | cookies = Cookies.initFromServlet( request, response );
22 | }
23 |
24 | @Test
25 | public void simple_value() {
26 | Mockito.when( request.getHeader( "cookie" ) ).thenReturn( "c=v" );
27 | String actual = cookies.get( "c" );
28 | String expected = "v";
29 | Assert.assertEquals( expected, actual );
30 | }
31 |
32 | @Test
33 | public void read_all() {
34 | Mockito.when( request.getHeader( "cookie" ) ).thenReturn( "c=v; foo=bar" );
35 | Map result = cookies.get();
36 |
37 | String actual = result.get( "c" );
38 | String expected = "v";
39 | Assert.assertEquals( expected, actual );
40 |
41 | actual = result.get( "foo" );
42 | expected = "bar";
43 | Assert.assertEquals( expected, actual );
44 | }
45 |
46 | @Test
47 | public void equal_sign_in_cookie_name() {
48 | Mockito.when( request.getHeader( "cookie" ) ).thenReturn( "c=a=b" );
49 |
50 | String actual = cookies.get( "c" );
51 | String expected = "a=b";
52 | Assert.assertEquals( expected, actual );
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/unit/CookiesWriteTest.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.unit;
2 |
3 | import org.joda.time.DateTime;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.mockito.Mockito;
8 | import org.mockito.runners.MockitoJUnitRunner;
9 |
10 | import com.github.jscookie.javacookie.Cookies;
11 | import com.github.jscookie.javacookie.Expiration;
12 | import com.github.jscookie.javacookie.Cookies.Attributes;
13 | import com.github.jscookie.javacookie.test.unit.utils.BaseTest;
14 |
15 | @RunWith( MockitoJUnitRunner.class )
16 | public class CookiesWriteTest extends BaseTest {
17 | private Cookies cookies;
18 |
19 | @Before
20 | public void before() {
21 | cookies = Cookies.initFromServlet( request, response );
22 | }
23 |
24 | @Test
25 | public void simple_write() {
26 | cookies.set( "c", "v" );
27 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=v; Path=/" );
28 | }
29 |
30 | @Test
31 | public void simple_write_with_default_attributes() {
32 | cookies.defaults()
33 | .path( "/" )
34 | .domain( "site.com" )
35 | .secure( true );
36 | cookies.set( "c", "v" );
37 |
38 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=v; Path=/; Domain=site.com; Secure" );
39 | }
40 |
41 | @Test
42 | public void simple_write_with_attributes() {
43 | cookies.set( "c", "v", Cookies.Attributes.empty()
44 | .path( "/" )
45 | .domain( "example.com" )
46 | .secure( true )
47 | );
48 |
49 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=v; Path=/; Domain=example.com; Secure" );
50 | }
51 |
52 | @Test
53 | public void simple_write_overriding_default_attributes() {
54 | cookies.defaults()
55 | .path( "/path/" )
56 | .secure( true );
57 | cookies.set( "c", "v", Cookies.Attributes.empty()
58 | .path( "/" )
59 | );
60 |
61 | // Should consider default secure if not overriden
62 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=v; Path=/; Secure" );
63 | }
64 |
65 | @Test
66 | public void removing_default_path_should_fallback_to_whole_site() {
67 | cookies.defaults()
68 | .path( null );
69 | cookies.set( "c", "v" );
70 |
71 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=v; Path=/" );
72 | }
73 |
74 | @Test
75 | public void should_not_write_the_path_attribute_if_set_as_an_empty_string() {
76 | cookies.defaults()
77 | .path( "" );
78 | cookies.set( "c", "v" );
79 |
80 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=v" );
81 | }
82 |
83 | @Test
84 | public void expires_attribute() {
85 | DateTime date_2015_06_07_23h38m46s = new DateTime( 2015, 6, 7, 23, 38, 46 );
86 | cookies.set( "c", "v", Attributes.empty()
87 | .expires( Expiration.date( date_2015_06_07_23h38m46s ) )
88 | );
89 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=v; Path=/; Expires=Sun, 07 Jun 2015 23:38:46 GMT" );
90 | }
91 |
92 | @Test
93 | public void httponly_attribute() {
94 | cookies.set( "c", "v", Attributes.empty()
95 | .httpOnly( true )
96 | );
97 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=v; Path=/; HttpOnly" );
98 | }
99 |
100 | @Test
101 | public void sameSite_attribute() {
102 | cookies.set( "c", "v", Attributes.empty()
103 | .sameSite( "Lax" )
104 | );
105 | Mockito.verify( response ).addHeader( "Set-Cookie", "c=v; Path=/; SameSite=Lax" );
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/unit/utils/BaseTest.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.unit.utils;
2 |
3 | import javax.servlet.http.HttpServletRequest;
4 | import javax.servlet.http.HttpServletResponse;
5 |
6 | import org.mockito.Mock;
7 |
8 | public class BaseTest {
9 | protected @Mock HttpServletRequest request;
10 | protected @Mock HttpServletResponse response;
11 | }
12 |
--------------------------------------------------------------------------------
/src/test/java/com/github/jscookie/javacookie/test/unit/utils/IntegrationUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.jscookie.javacookie.test.unit.utils;
2 |
3 | import java.io.File;
4 |
5 | import org.jboss.shrinkwrap.api.ShrinkWrap;
6 | import org.jboss.shrinkwrap.api.spec.WebArchive;
7 | import org.jboss.shrinkwrap.resolver.api.maven.Maven;
8 |
9 | public class IntegrationUtils {
10 | public static WebArchive createCommonDeployment() {
11 | boolean RECURSIVE_TRUE = true;
12 | return ShrinkWrap.create( WebArchive.class )
13 | .addPackage( "com.github.jscookie.javacookie" )
14 | .addPackages( RECURSIVE_TRUE, "com.github.jscookie.javacookie.test.integration" )
15 | .addAsLibraries(
16 | Maven.resolver()
17 | .loadPomFromFile( "pom.xml" )
18 | .resolve(
19 | "joda-time:joda-time",
20 | "com.fasterxml.jackson.core:jackson-databind"
21 | )
22 | .withTransitivity()
23 | .as( File.class )
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/test/resources/arquillian.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | target/wildfly-20.0.1.Final
9 | -Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/test/resources/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | encoding
7 | com.github.jscookie.javacookie.test.integration.encoding.EncodingServlet
8 |
9 |
10 | encoding
11 | /encoding
12 |
13 |
14 |
--------------------------------------------------------------------------------