├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── README.md ├── docs ├── _config.yml └── a.txt ├── pom.xml └── src ├── main └── java │ └── org │ └── rockm │ └── blink │ ├── BlinkException.java │ ├── BlinkRequest.java │ ├── BlinkResponse.java │ ├── BlinkServer.java │ ├── ExceptionHandler.java │ ├── Method.java │ ├── QueryParamsExtractor.java │ ├── RequestHandler.java │ ├── Route.java │ ├── RouteNotFoundException.java │ ├── RouteRequestRunner.java │ ├── RoutesContainer.java │ ├── Server.java │ ├── httpserver │ ├── HttpExchangeBlinkRequest.java │ ├── HttpExchangeBlinkResponse.java │ ├── JavaHttpServer.java │ └── MultiMethodHttpHandler.java │ └── io │ ├── InputStreamReader.java │ └── MessageConverter.java └── test ├── groovy └── test │ └── groovy │ └── org │ └── rockm │ └── blink │ └── BlinkServerTest_Groovy.groovy ├── java ├── e2e │ └── org │ │ └── rockm │ │ └── blink │ │ ├── BlinkServerTest.java │ │ ├── ContentTypeFeatureTest.java │ │ ├── CookiesFeatureTest.java │ │ ├── DeleteFeatureTest.java │ │ ├── ErrorsFeatureTest.java │ │ ├── GetFeatureTest.java │ │ ├── HeadersFeatureTest.java │ │ ├── PathParametersFeatureTest.java │ │ ├── PostFeatureTest.java │ │ ├── PutFeatureTest.java │ │ ├── ResetFeatureTest.java │ │ └── support │ │ ├── FileUtil.java │ │ └── HttpUtil.java └── test │ └── org │ └── rockm │ └── blink │ ├── ExceptionHandlerTest.java │ ├── RouteRequestRunnerTest.java │ ├── RouteTest.java │ ├── RoutesContainerTest.java │ └── httpserver │ ├── AbstractHttpExchangeBlinkResponseTest.java │ ├── HttpExchangeBlinkRequestTest.java │ ├── HttpExchangeBlinkResponseTest.java │ ├── HttpExchangeBlinkResponseTest_NullType.java │ ├── HttpExchangeStub.java │ └── MultiMethodHttpHandlerTest.java └── resources └── blink-img.jpg /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | working_directory: ~/blink-repo 5 | docker: 6 | - image: circleci/openjdk:8-jdk 7 | 8 | steps: 9 | - checkout 10 | 11 | - restore_cache: 12 | key: blink-java-{{ checksum "pom.xml" }} 13 | 14 | - run: mvn dependency:go-offline 15 | 16 | - save_cache: 17 | paths: 18 | - ~/.m2 19 | key: blink-java-{{ checksum "pom.xml" }} 20 | 21 | - run: mvn package 22 | 23 | - store_test_results: 24 | path: target/surefire-reports 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | build 3 | out 4 | *~ 5 | *.iml 6 | .gradle 7 | target -------------------------------------------------------------------------------- /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. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # blink-java 2 | [![CircleCI](https://circleci.com/gh/rockem/blink-java.svg?style=svg)](https://circleci.com/gh/rockem/blink-java) 3 | 4 | blink is a simplified http server, made primarily for using in tests. 5 | It has no dependencies other than those that come with Oracle's Jdk 6 | and it's inspired by Spark and Sinatra frameworks. 7 | 8 | ## Dependency 9 | ### Maven 10 | ```xml 11 | 12 | 13 | jcenter 14 | https://jcenter.bintray.com/ 15 | 16 | 17 | ``` 18 | ```xml 19 | 20 | org.rockem 21 | blink-java 22 | 0.5.3 23 | 24 | ``` 25 | ### Gradle 26 | ```groovy 27 | repositories { 28 | jcenter() 29 | } 30 | ``` 31 | ```groovy 32 | dependencies { 33 | compile 'org.rockem:blink-java:0.5.3' 34 | } 35 | ``` 36 | 37 | ## Usage 38 | ### Hello World 39 | #### Java 40 | ```java 41 | new BlinkServer(1234) {{ 42 | get("/hello", (req, res) -> "Hello World"); 43 | }}; 44 | ``` 45 | #### Groovy 46 | ```groovy 47 | new BlinkServer(1234) {{ 48 | get("/hello", { req, res -> "Hello World" }) 49 | }} 50 | ``` 51 | ### Path parameters 52 | ```java 53 | new BlinkServer(1234) {{ 54 | delete("/hello/{id}", (req, res) -> "Delete " + req.pathParam("id")); 55 | }}; 56 | ``` 57 | ### Default content type 58 | ```java 59 | new BlinkServer(1234) {{ 60 | contentType("application/json") 61 | get("/hello", (req, res) -> "{\"greeting\": \"Hello World\"}"); 62 | }}; 63 | ``` 64 | ### Request 65 | ```java 66 | req.body() // request body 67 | req.param("name") // Query parameter 68 | req.pathParam("name") // Path parameter 69 | req.uri() // Request uri 70 | req.header("name") // header value 71 | req.cookie("name") // cookie value 72 | ``` 73 | ### Response 74 | ```java 75 | res.status(201) // set retrun status code 76 | res.header("name", "value") // Set header 77 | res.type("type") // Set content type 78 | res.cookie("name", "value") // Add/Update cookie 79 | ``` 80 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /docs/a.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rockem/blink-java/d1db45daa9b72a67d6ba02bfdeef48693f5a0365/docs/a.txt -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | org.rockem 7 | blink-java 8 | 0.5.3 9 | 2016 10 | jar 11 | blink-java 12 | 13 | 14 | The Apache Software License, Version 2.0 15 | http://www.apache.org/licenses/LICENSE-2.0.txt 16 | repo 17 | 18 | 19 | 20 | UTF-8 21 | UTF-8 22 | 23 | 24 | 25 | junit 26 | junit 27 | 4.13.1 28 | test 29 | 30 | 31 | org.hamcrest 32 | hamcrest-all 33 | 1.3 34 | test 35 | 36 | 37 | org.apache.httpcomponents 38 | httpclient 39 | 4.5.2 40 | test 41 | 42 | 43 | org.mockito 44 | mockito-all 45 | 1.9.5 46 | test 47 | 48 | 49 | commons-io 50 | commons-io 51 | 2.5 52 | test 53 | 54 | 55 | com.google.code.gson 56 | gson 57 | 2.7 58 | test 59 | 60 | 61 | org.codehaus.groovy 62 | groovy-all 63 | 2.5.6 64 | pom 65 | 66 | 67 | org.codehaus.groovy.modules.http-builder 68 | http-builder 69 | 0.7.1 70 | test 71 | 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-compiler-plugin 78 | 3.8.0 79 | 80 | groovy-eclipse-compiler 81 | 1.8 82 | 1.8 83 | 84 | 85 | 86 | org.codehaus.groovy 87 | groovy-eclipse-compiler 88 | 2.9.2-01 89 | 90 | 91 | org.codehaus.groovy 92 | groovy-eclipse-batch 93 | 2.5.6-01 94 | 95 | 96 | 97 | 98 | org.codehaus.groovy 99 | groovy-eclipse-compiler 100 | 2.9.2-01 101 | true 102 | 103 | 104 | org.apache.maven.plugins 105 | maven-source-plugin 106 | 107 | 108 | attach-sources 109 | 110 | jar 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | blink-java 120 | blink-java 121 | https://api.bintray.com/maven/rockem/maven/blink-java/;publish=1 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/BlinkException.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink; 2 | 3 | public class BlinkException extends RuntimeException { 4 | 5 | public BlinkException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/BlinkRequest.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink; 2 | 3 | import java.net.URI; 4 | 5 | public interface BlinkRequest { 6 | 7 | /** 8 | * @return Request body as string 9 | */ 10 | String body(); 11 | 12 | /** 13 | * Allow to retrieve query parameters 14 | * @param name The name of the parameter 15 | * @return The value of the specific parameter 16 | */ 17 | String param(String name); 18 | 19 | /** 20 | * Retrieve path parameters 21 | * @param name The name of the parameter 22 | * @return The value of the specific parameter 23 | */ 24 | String pathParam(String name); 25 | 26 | /** 27 | * @return request uri 28 | */ 29 | URI uri(); 30 | 31 | /** 32 | * Retrieve header values 33 | * @param name header name 34 | * @return header's value 35 | */ 36 | String header(String name); 37 | 38 | class HeaderNotFoundException extends BlinkException { 39 | 40 | public HeaderNotFoundException(String message) { 41 | super(message); 42 | } 43 | } 44 | 45 | /** 46 | * Retrieve a specific cookie value 47 | * @param name of cookie 48 | * @return cookie's value 49 | */ 50 | String cookie(String name); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/BlinkResponse.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink; 2 | 3 | public interface BlinkResponse { 4 | 5 | /** 6 | * Set the response status code 7 | * @param statusCode 8 | */ 9 | void status(int statusCode); 10 | 11 | /** 12 | * Add a response header 13 | * @param name header's name 14 | * @param value header's value 15 | */ 16 | void header(String name, String value); 17 | 18 | /** 19 | * Set the content tyoe of the response 20 | * @param contentType 21 | */ 22 | void type(String contentType); 23 | 24 | /** 25 | * Add a response cookie 26 | * @param name 27 | * @param value 28 | */ 29 | void cookie(String name, String value); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/BlinkServer.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink; 2 | 3 | import org.rockm.blink.httpserver.JavaHttpServer; 4 | 5 | import java.io.IOException; 6 | 7 | public class BlinkServer { 8 | 9 | private final Server server; 10 | private final RoutesContainer routesContainer = new RoutesContainer(); 11 | 12 | public BlinkServer(int port) { 13 | this.server = new JavaHttpServer(port, routesContainer); 14 | } 15 | 16 | public void get(String path, RequestHandler rh) throws IOException { 17 | registerRoute(path, Method.GET, rh); 18 | } 19 | 20 | private void registerRoute(String path, Method method, RequestHandler rh) throws IOException { 21 | server.startIfNeeded(); 22 | routesContainer.addRoute(new Route(method, path, rh)); 23 | } 24 | 25 | public void post(String path, RequestHandler rh) throws IOException { 26 | registerRoute(path, Method.POST, rh); 27 | } 28 | 29 | public void delete(String path, RequestHandler rh) throws IOException { 30 | registerRoute(path, Method.DELETE, rh); 31 | } 32 | 33 | public void put(String path, RequestHandler rh) throws IOException { 34 | registerRoute(path, Method.PUT, rh); 35 | } 36 | 37 | public void stop() { 38 | server.stop(); 39 | } 40 | 41 | public void reset() { 42 | routesContainer.clear(); 43 | server.setDefaultContentType(null); 44 | } 45 | 46 | public void contentType(String type) { 47 | server.setDefaultContentType(type); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/ExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class ExceptionHandler { 7 | public static final int BAD_REQUEST = 400; 8 | public static final int NOT_FOUND = 404; 9 | public static final int SERVER_ERROR = 500; 10 | 11 | private static final Map, Integer> expToStatus = new HashMap<>(); 12 | 13 | static { 14 | expToStatus.put(BlinkRequest.HeaderNotFoundException.class, BAD_REQUEST); 15 | expToStatus.put(RouteNotFoundException.class, NOT_FOUND); 16 | } 17 | 18 | public Object handle(Exception e, BlinkRequest request, BlinkResponse response) { 19 | response.status(expToStatus.getOrDefault(e.getClass(), SERVER_ERROR)); 20 | return e.getMessage(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/Method.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink; 2 | 3 | public enum Method { 4 | GET, PUT, POST, DELETE, PATCH 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/QueryParamsExtractor.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink; 2 | 3 | import java.util.Arrays; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | 7 | public class QueryParamsExtractor { 8 | 9 | private static final String EXP_SEPARATOR = "&"; 10 | private final String query; 11 | 12 | public QueryParamsExtractor(String queryStr) { 13 | this.query = queryStr; 14 | } 15 | 16 | public Map toMap() { 17 | return Arrays.stream(allQueryParams()).collect(Collectors.toMap( 18 | p -> p.split("=")[0], p -> p.split("=")[1] 19 | )); 20 | } 21 | 22 | private String[] allQueryParams() { 23 | if (query == null) { 24 | return new String[0]; 25 | } 26 | return query.split(EXP_SEPARATOR); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/RequestHandler.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink; 2 | 3 | import java.net.URISyntaxException; 4 | 5 | public interface RequestHandler { 6 | 7 | Object handleRequest(BlinkRequest request, BlinkResponse response); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/Route.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink; 2 | 3 | import java.util.*; 4 | import java.util.regex.Matcher; 5 | import java.util.regex.Pattern; 6 | import java.util.stream.IntStream; 7 | 8 | import static java.util.stream.Collectors.toMap; 9 | 10 | public class Route { 11 | private static final String PATH_PARAM_PLACEHOLDERS_REGEX = "\\{([A-Za-z][A-Za-z0-9]*)\\}"; 12 | private static final String PATH_PARAM_REGEX = "([A-Za-z\\-~\\.\\_0-9]+)"; 13 | private static final String PATH_PARAM_ID_REGEX = "\\{[A-Za-z][A-Za-z0-9]*}"; 14 | public static final String END_OF_LINE = "$"; 15 | private final String route; 16 | private final String routeRegex; 17 | private final Method method; 18 | private final RequestHandler handler; 19 | private final List paramKeys; 20 | 21 | public Route(Method method, String path, RequestHandler handler) { 22 | this.method = method; 23 | this.route = path; 24 | this.handler = handler; 25 | this.routeRegex = createRegexFor(route); 26 | this.paramKeys = extract(PATH_PARAM_PLACEHOLDERS_REGEX, route); 27 | } 28 | 29 | private String createRegexFor(String route) { 30 | String regexPath = route.replaceAll(PATH_PARAM_ID_REGEX, PATH_PARAM_REGEX); 31 | return regexPath.replaceAll("/", "\\\\/") + END_OF_LINE; 32 | } 33 | 34 | private List extract(String regex, String input) { 35 | Matcher v = Pattern.compile(regex).matcher(input); 36 | List values = new ArrayList<>(); 37 | while (v.find() && v.groupCount() > 0) { 38 | IntStream.range(0, v.groupCount()).boxed().forEach(i -> values.add(v.group( i +1))); 39 | } 40 | return values; 41 | } 42 | 43 | public boolean isMatchedPath(String path) { 44 | Pattern p = Pattern.compile(routeRegex); 45 | Matcher m = p.matcher(path); 46 | return m.find(); 47 | } 48 | 49 | public Map getParamsFor(String path) { 50 | List values = extract(routeRegex, path); 51 | return IntStream.range(0,values.size()).boxed().collect(toMap(paramKeys::get, values::get)); 52 | } 53 | 54 | public RequestHandler getHandler() { 55 | return handler; 56 | } 57 | 58 | public Method getMethod() { 59 | return method; 60 | } 61 | 62 | @Override 63 | public boolean equals(Object o) { 64 | if (this == o) return true; 65 | if (!(o instanceof Route)) return false; 66 | Route route1 = (Route) o; 67 | return Objects.equals(route, route1.route) && 68 | method == route1.method; 69 | } 70 | 71 | @Override 72 | public int hashCode() { 73 | return Objects.hash(route, method); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/RouteNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink; 2 | 3 | public class RouteNotFoundException extends BlinkException { 4 | 5 | public RouteNotFoundException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/RouteRequestRunner.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink; 2 | 3 | import java.net.URI; 4 | import java.util.Map; 5 | 6 | import static java.lang.String.format; 7 | 8 | public class RouteRequestRunner { 9 | public static final String NOT_FOUND_MESSAGE = "%s Not Found"; 10 | 11 | private final Route route; 12 | 13 | public RouteRequestRunner(Route route) { 14 | this.route = route; 15 | } 16 | 17 | public Object run(BlinkRequest request, BlinkResponse response) { 18 | Object responseBody; 19 | try { 20 | responseBody = handleRequest(request, response); 21 | } catch (Exception e) { 22 | responseBody = new ExceptionHandler().handle(e, request, response); 23 | } 24 | return responseBody; 25 | } 26 | 27 | private Object handleRequest(BlinkRequest request, BlinkResponse response) { 28 | Object responseBody; 29 | String path = request.uri().getPath(); 30 | validateRoute(path); 31 | responseBody = route.getHandler().handleRequest( 32 | new PathParamsBlinkRequest(request, route.getParamsFor(path)), 33 | response); 34 | return responseBody; 35 | } 36 | 37 | private void validateRoute(String path) { 38 | if(route == null) { 39 | throw new RouteNotFoundException(format(NOT_FOUND_MESSAGE, path)); 40 | } 41 | } 42 | 43 | private class PathParamsBlinkRequest implements BlinkRequest { 44 | private final BlinkRequest decoratedRequest; 45 | private final Map params; 46 | 47 | public PathParamsBlinkRequest(BlinkRequest request, Map params) { 48 | this.decoratedRequest = request; 49 | this.params = params; 50 | } 51 | 52 | @Override 53 | public String body() { 54 | return decoratedRequest.body(); 55 | } 56 | 57 | @Override 58 | public String param(String name) { 59 | return decoratedRequest.param(name); 60 | } 61 | 62 | @Override 63 | public String pathParam(String name) { 64 | return params.get(name); 65 | } 66 | 67 | @Override 68 | public URI uri() { 69 | return decoratedRequest.uri(); 70 | } 71 | 72 | @Override 73 | public String header(String name) { 74 | return decoratedRequest.header(name); 75 | } 76 | 77 | @Override 78 | public String cookie(String name) { 79 | return decoratedRequest.cookie(name); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/RoutesContainer.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink; 2 | 3 | import com.sun.net.httpserver.HttpExchange; 4 | 5 | import java.util.*; 6 | 7 | public class RoutesContainer { 8 | 9 | private Map> methodToRoutes = new HashMap<>(); 10 | 11 | public Route getRouteFor(String method, String path) { 12 | Optional> routes = Optional.ofNullable(methodToRoutes.get(Method.valueOf(method))); 13 | Optional route = routes.orElse(new HashSet<>()).stream() 14 | .filter(r -> r.isMatchedPath(path)) 15 | .findFirst(); 16 | return route.orElse(null); 17 | } 18 | 19 | public void addRoute(Route route) { 20 | Set routes = getRoutesFor(route.getMethod()); 21 | routes.remove(route); 22 | routes.add(route); 23 | } 24 | 25 | private Set getRoutesFor(Method method) { 26 | return methodToRoutes.computeIfAbsent(method, k -> new HashSet<>()); 27 | } 28 | 29 | public void clear() { 30 | methodToRoutes.clear(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/Server.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink; 2 | 3 | import java.io.IOException; 4 | 5 | public interface Server { 6 | 7 | void startIfNeeded() throws IOException; 8 | 9 | void stop(); 10 | 11 | void setDefaultContentType(String contentType); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/httpserver/HttpExchangeBlinkRequest.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink.httpserver; 2 | 3 | import com.sun.net.httpserver.HttpExchange; 4 | import org.rockm.blink.BlinkRequest; 5 | import org.rockm.blink.QueryParamsExtractor; 6 | import org.rockm.blink.io.InputStreamReader; 7 | 8 | import java.io.InputStream; 9 | import java.net.URI; 10 | import java.util.Arrays; 11 | import java.util.Map; 12 | import java.util.stream.Collectors; 13 | 14 | public class HttpExchangeBlinkRequest implements BlinkRequest { 15 | 16 | private static final String COOKIE_HEADER = "Cookie"; 17 | 18 | private final HttpExchange httpExchange; 19 | private final String body; 20 | private final Map queryParams; 21 | 22 | public HttpExchangeBlinkRequest(HttpExchange httpExchange) { 23 | this.httpExchange = httpExchange; 24 | body = getAsString(httpExchange.getRequestBody()); 25 | queryParams = new QueryParamsExtractor(httpExchange.getRequestURI().getQuery()).toMap(); 26 | } 27 | 28 | private String getAsString(InputStream requestBody) { 29 | return new InputStreamReader(requestBody).readAsString(); 30 | } 31 | 32 | @Override 33 | public String body() { 34 | return body; 35 | } 36 | 37 | @Override 38 | public String param(String name) { 39 | return queryParams.get(name); 40 | } 41 | 42 | @Override 43 | public String pathParam(String id) { 44 | throw new UnsupportedOperationException("Path queryParams are unsupported here "); 45 | } 46 | 47 | @Override 48 | public URI uri() { 49 | return httpExchange.getRequestURI(); 50 | } 51 | 52 | @Override 53 | public String header(String name) { 54 | validateHeaderExists(name); 55 | return httpExchange.getRequestHeaders().get(name).stream() 56 | .reduce((t, u) -> t + "," + u).get(); 57 | } 58 | 59 | private void validateHeaderExists(String name) { 60 | if (httpExchange.getRequestHeaders().get(name) == null) { 61 | throw new HeaderNotFoundException("Header: [" + name + "] does not exist in request"); 62 | } 63 | } 64 | 65 | @Override 66 | public String cookie(String name) { 67 | if (cookieHeaderExists()) { 68 | String[] collect = httpExchange.getRequestHeaders().get(COOKIE_HEADER).get(0).split(";"); 69 | return Arrays.stream(collect).collect(Collectors.toMap(x -> x.split("=")[0], x -> x.split("=")[1])).get(name); 70 | } 71 | return null; 72 | } 73 | 74 | private boolean cookieHeaderExists() { 75 | return httpExchange.getRequestHeaders().get(COOKIE_HEADER) != null; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/httpserver/HttpExchangeBlinkResponse.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink.httpserver; 2 | 3 | import com.sun.net.httpserver.Headers; 4 | import com.sun.net.httpserver.HttpExchange; 5 | import org.rockm.blink.BlinkResponse; 6 | import org.rockm.blink.io.MessageConverter; 7 | 8 | import java.io.IOException; 9 | import java.io.OutputStream; 10 | import java.util.Collections; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | public class HttpExchangeBlinkResponse implements BlinkResponse { 15 | public static final String CONTENT_TYPE = "Content-Type"; 16 | public static final String COOKIE = "Set-Cookie"; 17 | public static final int DEFAULT_STATUS = 200; 18 | 19 | private Object body = ""; 20 | private int status = DEFAULT_STATUS; 21 | private final String defaultContentType; 22 | private final Map headers = new HashMap<>(); 23 | 24 | public HttpExchangeBlinkResponse(String defaultContentType) { 25 | this.defaultContentType = defaultContentType; 26 | } 27 | 28 | public void apply(HttpExchange httpExchange) throws IOException { 29 | setResponseHeaders(httpExchange); 30 | byte[] bodyAsBytes = getBodyInBytes(); 31 | httpExchange.sendResponseHeaders(status, bodyAsBytes.length); 32 | setResponseBody(httpExchange, bodyAsBytes); 33 | } 34 | 35 | private void setResponseHeaders(HttpExchange httpExchange) throws IOException { 36 | Headers httpHeaders = httpExchange.getResponseHeaders(); 37 | setDefaultContentType(httpHeaders); 38 | headers.entrySet().forEach((e) -> 39 | httpHeaders.put(e.getKey(), Collections.singletonList(e.getValue()))); 40 | } 41 | 42 | private void setDefaultContentType(Headers httpHeaders) { 43 | if (defaultContentType != null) { 44 | httpHeaders.put(CONTENT_TYPE, Collections.singletonList(defaultContentType)); 45 | } 46 | } 47 | 48 | private byte[] getBodyInBytes() { 49 | return new MessageConverter(body).convert(); 50 | } 51 | 52 | private void setResponseBody(HttpExchange httpExchange, byte[] bytes) throws IOException { 53 | OutputStream responseBody = httpExchange.getResponseBody(); 54 | responseBody.write(bytes); 55 | responseBody.close(); 56 | } 57 | 58 | public void setBody(final Object body) { 59 | this.body = (body == null ? "" : body); 60 | } 61 | 62 | public void status(int statusCode) { 63 | this.status = statusCode; 64 | } 65 | 66 | public void header(String name, String value) { 67 | headers.put(name, value); 68 | } 69 | 70 | @Override 71 | public void type(String contentType) { 72 | headers.put(CONTENT_TYPE, contentType); 73 | } 74 | 75 | @Override 76 | public void cookie(String name, String value) { 77 | String cookieContent = name + "=" + value; 78 | headers.put(COOKIE, headers.containsKey(COOKIE) ? appendCookie(cookieContent) : cookieContent); 79 | } 80 | 81 | private String appendCookie(String cookieContent) { 82 | return headers.get(COOKIE) + ";" + cookieContent; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/httpserver/JavaHttpServer.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink.httpserver; 2 | 3 | import com.sun.net.httpserver.HttpExchange; 4 | import com.sun.net.httpserver.HttpHandler; 5 | import com.sun.net.httpserver.HttpServer; 6 | import org.rockm.blink.*; 7 | 8 | import java.io.IOException; 9 | import java.net.InetSocketAddress; 10 | 11 | public class JavaHttpServer implements Server { 12 | 13 | private static final int NUM_OF_SECS_WAIT_FOR_STOP = 1; 14 | 15 | private final int port; 16 | private final RoutesContainer routesContainer; 17 | 18 | private HttpServer httpServer; 19 | private volatile boolean running = false; 20 | private String defaultContentType; 21 | 22 | 23 | public JavaHttpServer(int port, RoutesContainer routesContainer) { 24 | this.port = port; 25 | this.routesContainer = routesContainer; 26 | } 27 | 28 | public void startIfNeeded() throws IOException { 29 | if (!running) { 30 | running = true; 31 | createHttpServerFor(); 32 | } 33 | } 34 | 35 | private void createHttpServerFor() throws IOException { 36 | httpServer = HttpServer.create(new InetSocketAddress(port), 0); 37 | httpServer.createContext("/", new MultiMethodHttpHandler()); 38 | httpServer.start(); 39 | } 40 | 41 | public void stop() { 42 | httpServer.stop(NUM_OF_SECS_WAIT_FOR_STOP); 43 | } 44 | 45 | @Override 46 | public void setDefaultContentType(String contentType) { 47 | this.defaultContentType = contentType; 48 | } 49 | 50 | private class MultiMethodHttpHandler implements HttpHandler { 51 | 52 | @Override 53 | public void handle(HttpExchange httpExchange) throws IOException { 54 | Route route = routesContainer.getRouteFor( 55 | httpExchange.getRequestMethod(), 56 | httpExchange.getRequestURI().getPath()); 57 | HttpExchangeBlinkResponse response = new HttpExchangeBlinkResponse(defaultContentType); 58 | HttpExchangeBlinkRequest request = new HttpExchangeBlinkRequest(httpExchange); 59 | response.setBody(new RouteRequestRunner(route).run(request, response)); 60 | response.apply(httpExchange); 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/httpserver/MultiMethodHttpHandler.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink.httpserver; 2 | 3 | import com.sun.net.httpserver.HttpExchange; 4 | import com.sun.net.httpserver.HttpHandler; 5 | import org.rockm.blink.*; 6 | 7 | import java.io.IOException; 8 | 9 | @Deprecated 10 | public class MultiMethodHttpHandler implements HttpHandler { 11 | 12 | private final RoutesContainer routesContainer = new RoutesContainer(); 13 | private String defaultContentType; 14 | 15 | @Override 16 | public void handle(HttpExchange httpExchange) throws IOException { 17 | Route route = routesContainer.getRouteFor( 18 | httpExchange.getRequestMethod(), 19 | httpExchange.getRequestURI().getPath()); 20 | HttpExchangeBlinkResponse response = new HttpExchangeBlinkResponse(defaultContentType); 21 | HttpExchangeBlinkRequest request = new HttpExchangeBlinkRequest(httpExchange); 22 | response.setBody(new RouteRequestRunner(route).run(request, response)); 23 | response.apply(httpExchange); 24 | } 25 | 26 | public void addHandler(String path, Method method, RequestHandler handler) { 27 | routesContainer.addRoute(new Route(method, path, handler)); 28 | } 29 | 30 | public void reset() { 31 | routesContainer.clear(); 32 | defaultContentType = null; 33 | } 34 | 35 | public void contentType(String type) { 36 | defaultContentType = type; 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/io/InputStreamReader.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink.io; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | public class InputStreamReader { 8 | 9 | private static final int BUFFER_SIZE = 16384; 10 | 11 | private InputStream requestBody; 12 | 13 | public InputStreamReader(InputStream requestBody) { 14 | this.requestBody = requestBody; 15 | } 16 | 17 | public String readAsString() { 18 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 19 | 20 | int nRead; 21 | byte[] data = new byte[BUFFER_SIZE]; 22 | 23 | try { 24 | while ((nRead = requestBody.read(data, 0, data.length)) != -1) { 25 | buffer.write(data, 0, nRead); 26 | } 27 | buffer.flush(); 28 | return buffer.toString("UTF-8"); 29 | } catch (IOException e) { 30 | e.printStackTrace(); 31 | } 32 | return ""; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/rockm/blink/io/MessageConverter.java: -------------------------------------------------------------------------------- 1 | package org.rockm.blink.io; 2 | 3 | public class MessageConverter { 4 | 5 | private final Object message; 6 | 7 | public MessageConverter(Object message) { 8 | this.message = message; 9 | } 10 | 11 | public byte[] convert() { 12 | byte[] bodyAsBytes; 13 | if(message instanceof byte[]) { 14 | bodyAsBytes = (byte[]) message; 15 | } else { 16 | bodyAsBytes = message.toString().getBytes(); 17 | } 18 | return bodyAsBytes; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/groovy/test/groovy/org/rockm/blink/BlinkServerTest_Groovy.groovy: -------------------------------------------------------------------------------- 1 | package test.groovy.org.rockm.blink 2 | 3 | import groovyx.net.http.RESTClient 4 | import org.junit.Test 5 | import org.rockm.blink.BlinkServer 6 | 7 | class BlinkServerTest_Groovy { 8 | 9 | static final int PORT = 1234 10 | 11 | RESTClient restClient = new RESTClient("http://localhost:$PORT/") 12 | 13 | @Test 14 | void shouldHandleRequestWithNoReturn() throws Exception { 15 | new BlinkServer(PORT) {{ 16 | get("/hello", { req, res -> noReturnValue() }) 17 | }} 18 | restClient.get(path: 'hello') 19 | // passed 20 | } 21 | 22 | void noReturnValue() { 23 | // do nothing 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/e2e/org/rockm/blink/BlinkServerTest.java: -------------------------------------------------------------------------------- 1 | package e2e.org.rockm.blink; 2 | 3 | import org.apache.http.client.HttpClient; 4 | import org.apache.http.impl.client.HttpClientBuilder; 5 | import org.junit.Before; 6 | import org.rockm.blink.BlinkServer; 7 | 8 | import static e2e.org.rockm.blink.support.HttpUtil.PORT; 9 | 10 | public class BlinkServerTest { 11 | 12 | protected static BlinkServer blinkServer; 13 | protected final HttpClient httpClient = HttpClientBuilder.create().build(); 14 | 15 | @Before 16 | public void setUp() throws Exception { 17 | if(blinkServer == null) { 18 | blinkServer = new BlinkServer(PORT); 19 | } 20 | blinkServer.reset(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/e2e/org/rockm/blink/ContentTypeFeatureTest.java: -------------------------------------------------------------------------------- 1 | package e2e.org.rockm.blink; 2 | 3 | import org.apache.http.HttpResponse; 4 | import org.apache.http.client.methods.HttpGet; 5 | import org.junit.Test; 6 | 7 | import static e2e.org.rockm.blink.support.HttpUtil.fullPath; 8 | import static org.hamcrest.core.Is.is; 9 | import static org.junit.Assert.assertThat; 10 | 11 | public class ContentTypeFeatureTest extends BlinkServerTest { 12 | 13 | @Test 14 | public void shouldSetDefaultContentType() throws Exception { 15 | blinkServer.contentType("application/json"); 16 | blinkServer.get("/json", (req, res) -> "HelloWorld"); 17 | HttpResponse response = httpClient.execute(new HttpGet(fullPath("/json"))); 18 | assertThat(response.getFirstHeader("Content-Type").getValue(), is("application/json")); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/e2e/org/rockm/blink/CookiesFeatureTest.java: -------------------------------------------------------------------------------- 1 | package e2e.org.rockm.blink; 2 | 3 | import org.apache.http.HttpResponse; 4 | import org.apache.http.client.methods.HttpGet; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import static e2e.org.rockm.blink.support.HttpUtil.fullPath; 9 | import static org.hamcrest.core.Is.is; 10 | 11 | public class CookiesFeatureTest extends BlinkServerTest { 12 | 13 | private static final String COOKIE_NAME = "name"; 14 | 15 | @Test 16 | public void sendAndRetrieveCookies() throws Exception { 17 | blinkServer.get("/cookies", (req, res) -> { 18 | res.cookie(COOKIE_NAME, req.cookie(COOKIE_NAME)); 19 | return ""; 20 | }); 21 | HttpGet get = new HttpGet(fullPath("/cookies")); 22 | get.addHeader("Cookie", COOKIE_NAME + "=kuku"); 23 | HttpResponse response = httpClient.execute(get); 24 | Assert.assertThat(response.getFirstHeader("Set-Cookie").getValue(), is(COOKIE_NAME + "=kuku")); 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/e2e/org/rockm/blink/DeleteFeatureTest.java: -------------------------------------------------------------------------------- 1 | package e2e.org.rockm.blink; 2 | 3 | import org.apache.http.client.methods.HttpDelete; 4 | import org.junit.Test; 5 | import org.rockm.blink.BlinkServer; 6 | 7 | import java.io.IOException; 8 | 9 | import static e2e.org.rockm.blink.support.HttpUtil.fullPath; 10 | import static org.hamcrest.core.Is.is; 11 | import static org.junit.Assert.assertThat; 12 | 13 | public class DeleteFeatureTest extends BlinkServerTest { 14 | 15 | @Test 16 | public void shouldDeleteWithPathParameters() throws Exception { 17 | KukuDeleteServerStub serverStub = new KukuDeleteServerStub(blinkServer); 18 | httpClient.execute(new HttpDelete(fullPath("/kukus/542"))); 19 | assertThat(serverStub.idToDelete, is("542")); 20 | } 21 | 22 | private class KukuDeleteServerStub { 23 | String idToDelete; 24 | 25 | KukuDeleteServerStub(BlinkServer server) throws IOException { 26 | server.delete("/kukus/{id}", (req, res) -> idToDelete = req.pathParam("id")); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/e2e/org/rockm/blink/ErrorsFeatureTest.java: -------------------------------------------------------------------------------- 1 | package e2e.org.rockm.blink; 2 | 3 | import org.apache.http.HttpResponse; 4 | import org.apache.http.HttpStatus; 5 | import org.apache.http.client.methods.HttpGet; 6 | import org.junit.Test; 7 | 8 | import static e2e.org.rockm.blink.support.HttpUtil.fullPath; 9 | import static org.hamcrest.core.Is.is; 10 | import static org.junit.Assert.assertThat; 11 | 12 | public class ErrorsFeatureTest extends BlinkServerTest { 13 | 14 | @Test 15 | public void shouldReturn404ForBadPath() throws Exception { 16 | blinkServer.get("/hello", (req, res) -> ""); 17 | HttpResponse response = httpClient.execute(new HttpGet(fullPath("/kululu"))); 18 | assertThat(response.getStatusLine().getStatusCode(), is(HttpStatus.SC_NOT_FOUND)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/e2e/org/rockm/blink/GetFeatureTest.java: -------------------------------------------------------------------------------- 1 | package e2e.org.rockm.blink; 2 | 3 | import org.apache.commons.io.IOUtils; 4 | import org.apache.http.HttpResponse; 5 | import org.apache.http.client.methods.HttpGet; 6 | import org.junit.Test; 7 | 8 | import static e2e.org.rockm.blink.support.HttpUtil.fullPath; 9 | import static e2e.org.rockm.blink.support.HttpUtil.getBodyFrom; 10 | import static org.hamcrest.core.Is.is; 11 | import static org.junit.Assert.assertThat; 12 | import static e2e.org.rockm.blink.support.FileUtil.fileInBytes; 13 | 14 | public class GetFeatureTest extends BlinkServerTest { 15 | 16 | @Test 17 | public void shouldGetAStringResponse() throws Exception { 18 | blinkServer.get("/hello", (req, res) -> "Hello World"); 19 | HttpResponse response = httpClient.execute(new HttpGet(fullPath("/hello"))); 20 | assertThat(getBodyFrom(response), is("Hello World")); 21 | } 22 | 23 | @Test 24 | public void shouldGetWithPathParameter() throws Exception { 25 | blinkServer.get("/hello", (req, res) -> "Hello " + req.param("name")); 26 | HttpResponse response = httpClient.execute(new HttpGet(fullPath("/hello?name=Popo"))); 27 | assertThat(getBodyFrom(response), is("Hello Popo")); 28 | } 29 | 30 | @Test 31 | public void shouldRetrieveImage() throws Exception { 32 | byte[] imageInBytes = fileInBytes("blink-img.jpg"); 33 | blinkServer.contentType("image/jpg"); 34 | blinkServer.get("/img", (req, res) -> imageInBytes); 35 | HttpResponse response = httpClient.execute(new HttpGet(fullPath("/img"))); 36 | assertThat(IOUtils.toByteArray(response.getEntity().getContent()), is(imageInBytes)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/e2e/org/rockm/blink/HeadersFeatureTest.java: -------------------------------------------------------------------------------- 1 | package e2e.org.rockm.blink; 2 | 3 | import org.apache.http.HttpResponse; 4 | import org.apache.http.HttpStatus; 5 | import org.apache.http.client.methods.HttpGet; 6 | import org.junit.Test; 7 | 8 | import static e2e.org.rockm.blink.support.HttpUtil.fullPath; 9 | import static org.hamcrest.core.Is.is; 10 | import static org.hamcrest.core.IsEqual.equalTo; 11 | import static org.junit.Assert.assertThat; 12 | 13 | public class HeadersFeatureTest extends BlinkServerTest { 14 | 15 | @Test 16 | public void shouldReturnBadRequestWhenFetchingNonExistingHeader() throws Exception { 17 | blinkServer.get("/kuku", (req, res) -> req.header("stam")); 18 | HttpResponse response = httpClient.execute(new HttpGet(fullPath("/kuku"))); 19 | assertThat(response.getStatusLine().getStatusCode(), is(HttpStatus.SC_BAD_REQUEST)); 20 | } 21 | 22 | @Test 23 | public void shouldRetrieveResponseHeader() throws Exception { 24 | blinkServer.get("/hello", (req, res) -> { 25 | res.header("Content-Type", "application/json"); 26 | return ""; 27 | }); 28 | HttpGet httpGet = new HttpGet(fullPath("/hello")); 29 | HttpResponse response = httpClient.execute(httpGet); 30 | assertThat(response.getFirstHeader("Content-type").getValue(), equalTo("application/json")); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/e2e/org/rockm/blink/PathParametersFeatureTest.java: -------------------------------------------------------------------------------- 1 | package e2e.org.rockm.blink; 2 | 3 | public class PathParametersFeatureTest extends BlinkServerTest { 4 | 5 | 6 | } 7 | -------------------------------------------------------------------------------- /src/test/java/e2e/org/rockm/blink/PostFeatureTest.java: -------------------------------------------------------------------------------- 1 | package e2e.org.rockm.blink; 2 | 3 | import org.apache.http.HttpEntity; 4 | import org.apache.http.HttpResponse; 5 | import org.apache.http.client.methods.HttpPost; 6 | import org.apache.http.entity.ByteArrayEntity; 7 | import org.apache.http.entity.StringEntity; 8 | import org.apache.http.message.BasicHeader; 9 | import org.junit.Test; 10 | 11 | import java.io.UnsupportedEncodingException; 12 | 13 | import static e2e.org.rockm.blink.support.HttpUtil.fullPath; 14 | import static e2e.org.rockm.blink.support.HttpUtil.getBodyFrom; 15 | import static org.hamcrest.core.Is.is; 16 | import static org.junit.Assert.assertThat; 17 | 18 | public class PostFeatureTest extends BlinkServerTest { 19 | 20 | @Test 21 | public void shouldEchoStringPostRequest() throws Exception { 22 | blinkServer.post("/hello", (req, res) -> req.body()); 23 | HttpResponse response = httpClient.execute(createHttpPost("/hello", new StringEntity("Kuku"))); 24 | assertThat(getBodyFrom(response), is("Kuku")); 25 | } 26 | 27 | private HttpPost createHttpPost(String path, HttpEntity entity) throws UnsupportedEncodingException { 28 | HttpPost request = new HttpPost(fullPath(path)); 29 | request.setEntity(entity); 30 | return request; 31 | } 32 | 33 | @Test 34 | public void shouldEchoByteArrayWithPostRequest() throws Exception { 35 | blinkServer.post("/hello", (req, res) -> req.body()); 36 | HttpPost request = new HttpPost(fullPath("/hello")); 37 | request.addHeader(new BasicHeader("Content-Type", "application/json")); 38 | request.setEntity(new ByteArrayEntity("{\"p\":\"kuku\"}".getBytes())); 39 | HttpResponse response = httpClient.execute(request); 40 | assertThat(getBodyFrom(response), is("{\"p\":\"kuku\"}")); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/e2e/org/rockm/blink/PutFeatureTest.java: -------------------------------------------------------------------------------- 1 | package e2e.org.rockm.blink; 2 | 3 | import org.apache.http.HttpResponse; 4 | import org.apache.http.client.methods.HttpPut; 5 | import org.apache.http.entity.StringEntity; 6 | import org.junit.Test; 7 | 8 | import static e2e.org.rockm.blink.support.HttpUtil.fullPath; 9 | import static e2e.org.rockm.blink.support.HttpUtil.getBodyFrom; 10 | import static org.hamcrest.core.Is.is; 11 | import static org.junit.Assert.assertThat; 12 | 13 | public class PutFeatureTest extends BlinkServerTest { 14 | 15 | @Test 16 | public void shouldEchoPutRequest() throws Exception { 17 | blinkServer.put("/hello", (req, res) -> req.body()); 18 | HttpPut request = new HttpPut(fullPath("/hello")); 19 | request.setEntity(new StringEntity("Dudu")); 20 | HttpResponse response = httpClient.execute(request); 21 | assertThat(getBodyFrom(response), is("Dudu")); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/e2e/org/rockm/blink/ResetFeatureTest.java: -------------------------------------------------------------------------------- 1 | package e2e.org.rockm.blink; 2 | 3 | import org.apache.http.HttpResponse; 4 | import org.apache.http.HttpStatus; 5 | import org.apache.http.client.methods.HttpGet; 6 | import org.junit.Test; 7 | 8 | import static e2e.org.rockm.blink.support.HttpUtil.fullPath; 9 | import static org.hamcrest.core.Is.is; 10 | import static org.junit.Assert.assertNull; 11 | import static org.junit.Assert.assertThat; 12 | 13 | public class ResetFeatureTest extends BlinkServerTest { 14 | 15 | @Test 16 | public void shouldResetRouting() throws Exception { 17 | blinkServer.get("/hello", (req, res) -> "Hello World"); 18 | blinkServer.reset(); 19 | HttpResponse response = httpClient.execute(new HttpGet(fullPath("/hello"))); 20 | assertThat(response.getStatusLine().getStatusCode(), is(HttpStatus.SC_NOT_FOUND)); 21 | } 22 | 23 | @Test 24 | public void shouldResetContentType() throws Exception { 25 | blinkServer.contentType("application/json"); 26 | blinkServer.reset(); 27 | blinkServer.get("/json", (req, res) -> "HelloWorld"); 28 | HttpResponse response = httpClient.execute(new HttpGet(fullPath("/json"))); 29 | assertNull(response.getFirstHeader("Content-Type")); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/e2e/org/rockm/blink/support/FileUtil.java: -------------------------------------------------------------------------------- 1 | package e2e.org.rockm.blink.support; 2 | 3 | import java.io.IOException; 4 | import java.net.URISyntaxException; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.nio.file.Paths; 8 | 9 | public class FileUtil { 10 | 11 | public static byte[] fileInBytes(String fileName) throws URISyntaxException, IOException { 12 | Path path = Paths.get(ClassLoader.getSystemResource(fileName).toURI()); 13 | return Files.readAllBytes(path); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/e2e/org/rockm/blink/support/HttpUtil.java: -------------------------------------------------------------------------------- 1 | package e2e.org.rockm.blink.support; 2 | 3 | import org.apache.commons.io.IOUtils; 4 | import org.apache.http.HttpResponse; 5 | 6 | import java.io.IOException; 7 | 8 | import static java.lang.String.format; 9 | 10 | public class HttpUtil { 11 | 12 | public static final int PORT = 4567; 13 | public static final String DOMAIN = "http://localhost:" + PORT; 14 | 15 | public static String fullPath(String path) { 16 | return format("%s" + path, DOMAIN); 17 | } 18 | 19 | public static String getBodyFrom(HttpResponse response) throws IOException { 20 | return IOUtils.toString(response.getEntity().getContent(), "UTF-8"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/test/org/rockm/blink/ExceptionHandlerTest.java: -------------------------------------------------------------------------------- 1 | package test.org.rockm.blink; 2 | 3 | import org.junit.Test; 4 | import org.rockm.blink.BlinkRequest; 5 | import org.rockm.blink.BlinkResponse; 6 | import org.rockm.blink.ExceptionHandler; 7 | 8 | import static org.hamcrest.core.Is.is; 9 | import static org.junit.Assert.*; 10 | import static org.mockito.Mockito.mock; 11 | import static org.mockito.Mockito.verify; 12 | 13 | public class ExceptionHandlerTest { 14 | 15 | private static final String ERROR_MESSAGE = "message"; 16 | 17 | private final BlinkRequest request = mock(BlinkRequest.class); 18 | private final BlinkResponse response = mock(BlinkResponse.class); 19 | 20 | @Test 21 | public void shouldHandleUnexpectedError() throws Exception { 22 | ExceptionHandler eh = new ExceptionHandler(); 23 | Object responseBody = eh.handle(new Exception(ERROR_MESSAGE), request, response); 24 | assertThat(responseBody, is(ERROR_MESSAGE)); 25 | verify(response).status(ExceptionHandler.SERVER_ERROR); 26 | } 27 | } -------------------------------------------------------------------------------- /src/test/java/test/org/rockm/blink/RouteRequestRunnerTest.java: -------------------------------------------------------------------------------- 1 | package test.org.rockm.blink; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import org.rockm.blink.*; 6 | 7 | import java.net.URI; 8 | 9 | import static java.lang.String.format; 10 | import static org.hamcrest.core.Is.is; 11 | import static org.junit.Assert.assertThat; 12 | import static org.mockito.Matchers.any; 13 | import static org.mockito.Mockito.*; 14 | import static org.rockm.blink.ExceptionHandler.BAD_REQUEST; 15 | import static org.rockm.blink.ExceptionHandler.NOT_FOUND; 16 | 17 | public class RouteRequestRunnerTest { 18 | 19 | private static final String HEADER_NOR_FOUND_MESSAGE = "error message"; 20 | private static final String PATH = "/lala"; 21 | private RequestHandler requestHandler = mock(RequestHandler.class); 22 | private BlinkRequest request = mock(BlinkRequest.class); 23 | private BlinkResponse response = mock(BlinkResponse.class); 24 | 25 | @Before 26 | public void setUp() throws Exception { 27 | when(request.uri()).thenReturn(URI.create("http://a.com" + PATH)); 28 | } 29 | 30 | @Test 31 | public void shouldSetResponseBadRequestAndMessage() throws Exception { 32 | Route route = new Route(Method.GET, "/popo", requestHandler); 33 | when(requestHandler.handleRequest(any(), any())).thenThrow(new BlinkRequest.HeaderNotFoundException(HEADER_NOR_FOUND_MESSAGE)); 34 | String body = (String) runWith(route); 35 | assertThat(body, is(HEADER_NOR_FOUND_MESSAGE)); 36 | verify(response).status(BAD_REQUEST); 37 | } 38 | 39 | private Object runWith(Route route) { 40 | return new RouteRequestRunner(route).run(request, response); 41 | } 42 | 43 | @Test 44 | public void shouldSetResponseAsNotFound() throws Exception { 45 | String body = (String) runWith(null); 46 | assertThat(body, is(format(RouteRequestRunner.NOT_FOUND_MESSAGE, PATH))); 47 | verify(response).status(NOT_FOUND); 48 | 49 | } 50 | } -------------------------------------------------------------------------------- /src/test/java/test/org/rockm/blink/RouteTest.java: -------------------------------------------------------------------------------- 1 | package test.org.rockm.blink; 2 | 3 | import org.junit.Test; 4 | import org.rockm.blink.Method; 5 | import org.rockm.blink.Route; 6 | 7 | import static org.hamcrest.core.Is.is; 8 | import static org.hamcrest.core.IsNot.not; 9 | import static org.junit.Assert.assertThat; 10 | 11 | public class RouteTest { 12 | 13 | @Test 14 | public void shouldRetrievePathParamValue() throws Exception { 15 | Route route = new Route(Method.DELETE, "/hello/{id}", null); 16 | assertThat(route.getParamsFor("/hello/543").get("id"), is("543")); 17 | } 18 | 19 | @Test 20 | public void shouldRetrieveMoreThanOnePathParamValue() throws Exception { 21 | Route route = new Route(Method.DELETE, "/hello/{id}/{name}", null); 22 | assertThat(route.getParamsFor("/hello/543/kuku").get("id"), is("543")); 23 | assertThat(route.getParamsFor("/hello/543/kuku").get("name"), is("kuku")); 24 | } 25 | 26 | @Test 27 | public void shouldAllowMoreCharsInPathParams() throws Exception { 28 | Route route = new Route(Method.GET, "/hello/{id}", null); 29 | String paramWithAllAllowedChars = "a5-._~Lulu"; 30 | assertThat(route.getParamsFor("/hello/" + paramWithAllAllowedChars).get("id"), is(paramWithAllAllowedChars)); 31 | } 32 | 33 | @Test 34 | public void shouldMatchExactPath() throws Exception { 35 | Route route = new Route(Method.GET, "/hello", null); 36 | assertThat(route.isMatchedPath("/hello"), is(true)); 37 | assertThat(route.isMatchedPath("/hello123"), is(false)); 38 | } 39 | 40 | @Test 41 | public void shouldNotMatchPathWithMoreElements() throws Exception { 42 | Route route = new Route(Method.GET, "/hello", null); 43 | assertThat(route.isMatchedPath("/hello/1/"), is(false)); 44 | 45 | } 46 | } -------------------------------------------------------------------------------- /src/test/java/test/org/rockm/blink/RoutesContainerTest.java: -------------------------------------------------------------------------------- 1 | package test.org.rockm.blink; 2 | 3 | import org.junit.Test; 4 | import org.rockm.blink.Method; 5 | import org.rockm.blink.Route; 6 | import org.rockm.blink.RoutesContainer; 7 | 8 | import static org.hamcrest.core.Is.is; 9 | import static org.hamcrest.core.IsNot.not; 10 | import static org.junit.Assert.assertNull; 11 | import static org.junit.Assert.assertThat; 12 | 13 | public class RoutesContainerTest { 14 | 15 | @Test 16 | public void shouldRetrieveNullWhenNoRoutes() throws Exception { 17 | assertNull(new RoutesContainer().getRouteFor("GET", "http://stam")); 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /src/test/java/test/org/rockm/blink/httpserver/AbstractHttpExchangeBlinkResponseTest.java: -------------------------------------------------------------------------------- 1 | package test.org.rockm.blink.httpserver; 2 | 3 | import com.sun.net.httpserver.Headers; 4 | import com.sun.net.httpserver.HttpExchange; 5 | import org.apache.http.cookie.Cookie; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.rockm.blink.BlinkResponse; 9 | import org.rockm.blink.httpserver.HttpExchangeBlinkResponse; 10 | 11 | import java.io.ByteArrayOutputStream; 12 | import java.io.IOException; 13 | import java.net.HttpCookie; 14 | import java.util.Arrays; 15 | import java.util.HashSet; 16 | import java.util.List; 17 | import java.util.Set; 18 | 19 | import static org.junit.Assert.assertNull; 20 | import static org.mockito.Mockito.mock; 21 | import static org.mockito.Mockito.when; 22 | import static org.rockm.blink.httpserver.HttpExchangeBlinkResponse.CONTENT_TYPE; 23 | 24 | public class AbstractHttpExchangeBlinkResponseTest { 25 | 26 | protected BlinkResponse blinkResponse; 27 | protected final HttpExchange httpExchange = mock(HttpExchange.class); 28 | protected final ByteArrayOutputStream responseOutputStream = new ByteArrayOutputStream(); 29 | protected final Headers headers = new Headers(); 30 | 31 | public void setUp() throws Exception { 32 | when(httpExchange.getResponseBody()).thenReturn(responseOutputStream); 33 | when(httpExchange.getResponseHeaders()).thenReturn(headers); 34 | } 35 | 36 | protected void apply() throws IOException { 37 | ((HttpExchangeBlinkResponse)blinkResponse).apply(httpExchange); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/test/org/rockm/blink/httpserver/HttpExchangeBlinkRequestTest.java: -------------------------------------------------------------------------------- 1 | package test.org.rockm.blink.httpserver; 2 | 3 | import com.google.gson.Gson; 4 | import com.sun.net.httpserver.Headers; 5 | import com.sun.net.httpserver.HttpExchange; 6 | import org.junit.Before; 7 | import org.junit.Ignore; 8 | import org.junit.Test; 9 | import org.rockm.blink.BlinkRequest; 10 | import org.rockm.blink.httpserver.HttpExchangeBlinkRequest; 11 | 12 | import java.io.ByteArrayInputStream; 13 | import java.net.URI; 14 | import java.util.Arrays; 15 | 16 | import static org.hamcrest.core.Is.is; 17 | import static org.junit.Assert.*; 18 | import static org.mockito.Mockito.mock; 19 | import static org.mockito.Mockito.when; 20 | 21 | public class HttpExchangeBlinkRequestTest { 22 | 23 | private static final String BODY = "kuku kuku"; 24 | private final HttpExchange httpExchange = mock(HttpExchange.class); 25 | private final Headers headers = new Headers(); 26 | 27 | @Before 28 | public void setUp() throws Exception { 29 | when(httpExchange.getRequestHeaders()).thenReturn(headers); 30 | when(httpExchange.getRequestBody()).thenReturn(new ByteArrayInputStream(BODY.getBytes())); 31 | when(httpExchange.getRequestURI()).thenReturn(URI.create("http://domain.com")); 32 | } 33 | 34 | @Test 35 | public void shouldRetrieveHeaderByName() throws Exception { 36 | headers.put("key", Arrays.asList("kuku")); 37 | assertThat(request().header("key"), is("kuku")); 38 | } 39 | 40 | private HttpExchangeBlinkRequest request() { 41 | return new HttpExchangeBlinkRequest(httpExchange); 42 | } 43 | 44 | @Test 45 | public void shouldRetrieveMultipleValuesHeaderByName() throws Exception { 46 | headers.put("key", Arrays.asList("kuku1", "kuku2")); 47 | assertThat(request().header("key"), is("kuku1,kuku2")); 48 | } 49 | 50 | @Test(expected = BlinkRequest.HeaderNotFoundException.class) 51 | public void shouldFailWhenReadNonExistingHeader() throws Exception { 52 | assertNull(request().header("lala")); 53 | } 54 | 55 | @Test 56 | public void shouldBeAbleToReadBodyMoreThanOnce() throws Exception { 57 | HttpExchangeBlinkRequest request = request(); 58 | assertThat(request.body(), is(BODY)); 59 | assertThat(request.body(), is(BODY)); 60 | } 61 | 62 | @Test 63 | public void shouldRetrieveQueryParamsByName() throws Exception { 64 | when(httpExchange.getRequestURI()).thenReturn(URI.create("http://domain.com?name=popo&type=cool")); 65 | HttpExchangeBlinkRequest request = request(); 66 | assertThat(request.param("name"), is("popo")); 67 | assertThat(request.param("type"), is("cool")); 68 | } 69 | 70 | @Test 71 | public void shouldRetrieveCookieByName() throws Exception { 72 | headers.put("Cookie", Arrays.asList("kuku=popo;crazy=cookie_value_for_crazy")); 73 | assertThat(request().cookie("crazy"), is("cookie_value_for_crazy")); 74 | } 75 | 76 | @Test 77 | public void shouldReturnNullWhenCookieNotFound(){ 78 | assertNull(request().cookie("cookie_key_not_found")); 79 | } 80 | } -------------------------------------------------------------------------------- /src/test/java/test/org/rockm/blink/httpserver/HttpExchangeBlinkResponseTest.java: -------------------------------------------------------------------------------- 1 | package test.org.rockm.blink.httpserver; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import org.rockm.blink.httpserver.HttpExchangeBlinkResponse; 6 | 7 | import java.io.IOException; 8 | import java.util.Arrays; 9 | 10 | import static org.hamcrest.core.Is.is; 11 | import static org.junit.Assert.*; 12 | import static org.mockito.Matchers.any; 13 | import static org.mockito.Mockito.*; 14 | import static org.rockm.blink.httpserver.HttpExchangeBlinkResponse.CONTENT_TYPE; 15 | 16 | public class HttpExchangeBlinkResponseTest extends AbstractHttpExchangeBlinkResponseTest { 17 | 18 | private static final String DEFAULT_CONTENT_TYPE = "app/json"; 19 | 20 | @Before 21 | public void setUp() throws Exception { 22 | super.setUp(); 23 | blinkResponse = new HttpExchangeBlinkResponse(DEFAULT_CONTENT_TYPE); 24 | } 25 | 26 | @Test 27 | public void bodyShouldNotBeNull() throws Exception { 28 | apply(); 29 | assertThat(responseOutputStream.toString("UTF-8"), is("")); 30 | } 31 | 32 | @Test 33 | public void shouldHandleNullBody() throws Exception { 34 | ((HttpExchangeBlinkResponse)blinkResponse).setBody(null); 35 | apply(); 36 | assertThat(responseOutputStream.toString("UTF-8"), is("")); 37 | } 38 | 39 | @Test 40 | public void shouldSetStatusCode() throws Exception { 41 | blinkResponse.status(404); 42 | apply(); 43 | verify(httpExchange).sendResponseHeaders(404, 0); 44 | } 45 | 46 | @Test 47 | public void defaultStatusShouldBe200() throws Exception { 48 | apply(); 49 | verify(httpExchange).sendResponseHeaders(200, 0); 50 | } 51 | 52 | @Test 53 | public void shouldAddHeadersToResponse() throws Exception { 54 | blinkResponse.header("content", "kuku"); 55 | blinkResponse.header("Accept", "popo"); 56 | apply(); 57 | assertThat(headers.get("content"), is(Arrays.asList("kuku"))); 58 | assertThat(headers.get("Accept"), is(Arrays.asList("popo"))); 59 | } 60 | 61 | @Test 62 | public void shouldAddCookieToResponse() throws IOException { 63 | blinkResponse.cookie("myCookie", "zabo"); 64 | apply(); 65 | assertThat(headers.get("Set-Cookie"), is(Arrays.asList("myCookie=zabo"))); 66 | } 67 | 68 | @Test 69 | public void shouldAddNumberOfCookiesToResponse() throws IOException { 70 | blinkResponse.cookie("myCookie", "zabo"); 71 | blinkResponse.cookie("mySecondCookie", "kuku"); 72 | apply(); 73 | assertThat(headers.get("Set-Cookie"), is(Arrays.asList("myCookie=zabo;mySecondCookie=kuku"))); 74 | } 75 | 76 | @Test 77 | public void shouldSetContentTypeHeader() throws Exception { 78 | blinkResponse.type("application/json"); 79 | apply(); 80 | assertThat(headers.get(CONTENT_TYPE), is(Arrays.asList("application/json"))); 81 | } 82 | 83 | @Test 84 | public void shouldSetDefaultContentType() throws Exception { 85 | apply(); 86 | assertThat(headers.get(CONTENT_TYPE), is(Arrays.asList(DEFAULT_CONTENT_TYPE))); 87 | } 88 | 89 | @Test 90 | public void shouldRetrieveByteArray() throws Exception { 91 | byte[] bytes = new byte[] {3, 2, 45, 23, 67, 12}; 92 | ((HttpExchangeBlinkResponse)blinkResponse).setBody(bytes); 93 | apply(); 94 | assertThat(responseOutputStream.toByteArray(), is(bytes)); 95 | } 96 | } -------------------------------------------------------------------------------- /src/test/java/test/org/rockm/blink/httpserver/HttpExchangeBlinkResponseTest_NullType.java: -------------------------------------------------------------------------------- 1 | package test.org.rockm.blink.httpserver; 2 | 3 | import com.sun.net.httpserver.Headers; 4 | import com.sun.net.httpserver.HttpExchange; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | import org.rockm.blink.BlinkResponse; 8 | import org.rockm.blink.httpserver.HttpExchangeBlinkResponse; 9 | 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | import java.util.Arrays; 13 | 14 | import static org.hamcrest.core.Is.is; 15 | import static org.junit.Assert.assertNull; 16 | import static org.junit.Assert.assertThat; 17 | import static org.mockito.Mockito.*; 18 | import static org.rockm.blink.httpserver.HttpExchangeBlinkResponse.CONTENT_TYPE; 19 | 20 | public class HttpExchangeBlinkResponseTest_NullType extends AbstractHttpExchangeBlinkResponseTest { 21 | 22 | @Before 23 | public void setUp() throws Exception { 24 | super.setUp(); 25 | blinkResponse = new HttpExchangeBlinkResponse(null); 26 | } 27 | 28 | @Test 29 | public void shouldNotSetDefaultContentTypeWhenNull() throws Exception { 30 | apply(); 31 | assertNull(headers.get(CONTENT_TYPE)); 32 | } 33 | } -------------------------------------------------------------------------------- /src/test/java/test/org/rockm/blink/httpserver/HttpExchangeStub.java: -------------------------------------------------------------------------------- 1 | package test.org.rockm.blink.httpserver; 2 | 3 | import com.sun.net.httpserver.Headers; 4 | import com.sun.net.httpserver.HttpContext; 5 | import com.sun.net.httpserver.HttpExchange; 6 | import com.sun.net.httpserver.HttpPrincipal; 7 | import org.apache.http.HttpHeaders; 8 | 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.OutputStream; 12 | import java.net.InetSocketAddress; 13 | import java.net.URI; 14 | 15 | public class HttpExchangeStub extends HttpExchange { 16 | 17 | public URI requestURI; 18 | public OutputStream responseBody; 19 | public int statusCode; 20 | public long bodySize; 21 | public Headers requestHeaders = new Headers(); 22 | public Headers responseHeaders = new Headers(); 23 | 24 | @Override 25 | public Headers getRequestHeaders() { 26 | return requestHeaders; 27 | } 28 | 29 | @Override 30 | public Headers getResponseHeaders() { 31 | return responseHeaders; 32 | } 33 | 34 | @Override 35 | public URI getRequestURI() { 36 | return requestURI; 37 | } 38 | 39 | @Override 40 | public String getRequestMethod() { 41 | return null; 42 | } 43 | 44 | @Override 45 | public HttpContext getHttpContext() { 46 | return null; 47 | } 48 | 49 | @Override 50 | public void close() { 51 | 52 | } 53 | 54 | @Override 55 | public InputStream getRequestBody() { 56 | return null; 57 | } 58 | 59 | @Override 60 | public OutputStream getResponseBody() { 61 | return responseBody; 62 | } 63 | 64 | @Override 65 | public void sendResponseHeaders(int i, long l) throws IOException { 66 | statusCode = i; 67 | bodySize = l; 68 | } 69 | 70 | @Override 71 | public InetSocketAddress getRemoteAddress() { 72 | return null; 73 | } 74 | 75 | @Override 76 | public int getResponseCode() { 77 | return 0; 78 | } 79 | 80 | @Override 81 | public InetSocketAddress getLocalAddress() { 82 | return null; 83 | } 84 | 85 | @Override 86 | public String getProtocol() { 87 | return null; 88 | } 89 | 90 | @Override 91 | public Object getAttribute(String s) { 92 | return null; 93 | } 94 | 95 | @Override 96 | public void setAttribute(String s, Object o) { 97 | 98 | } 99 | 100 | @Override 101 | public void setStreams(InputStream inputStream, OutputStream outputStream) { 102 | 103 | } 104 | 105 | @Override 106 | public HttpPrincipal getPrincipal() { 107 | return null; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/test/java/test/org/rockm/blink/httpserver/MultiMethodHttpHandlerTest.java: -------------------------------------------------------------------------------- 1 | package test.org.rockm.blink.httpserver; 2 | 3 | import com.sun.net.httpserver.HttpExchange; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.rockm.blink.BlinkRequest; 7 | import org.rockm.blink.BlinkResponse; 8 | import org.rockm.blink.Method; 9 | import org.rockm.blink.httpserver.MultiMethodHttpHandler; 10 | import org.rockm.blink.RequestHandler; 11 | 12 | import java.io.ByteArrayInputStream; 13 | import java.io.ByteArrayOutputStream; 14 | import java.net.URI; 15 | 16 | import static org.hamcrest.core.Is.is; 17 | import static org.junit.Assert.*; 18 | import static org.mockito.Matchers.anyLong; 19 | import static org.mockito.Matchers.eq; 20 | import static org.mockito.Mockito.mock; 21 | import static org.mockito.Mockito.verify; 22 | import static org.mockito.Mockito.when; 23 | 24 | public class MultiMethodHttpHandlerTest { 25 | 26 | private static final String PATH_WITH_PARAM = "/blinks/{id}"; 27 | private static final String PARAM_ID = "234"; 28 | 29 | private final HttpExchange httpExchange = mock(HttpExchange.class); 30 | private final MultiMethodHttpHandler handler = new MultiMethodHttpHandler(); 31 | private final StubRequestHandler requestHandler = new StubRequestHandler(); 32 | 33 | @Before 34 | public void setUp() throws Exception { 35 | when(httpExchange.getResponseBody()).thenReturn(new ByteArrayOutputStream()); 36 | when(httpExchange.getRequestBody()).thenReturn(new ByteArrayInputStream("".getBytes())); 37 | } 38 | 39 | @Test 40 | public void shouldRetrievePathParam() throws Exception { 41 | handler.addHandler(PATH_WITH_PARAM, Method.DELETE, requestHandler); 42 | when(httpExchange.getRequestURI()).thenReturn(URI.create("http://domain.com/blinks/" + PARAM_ID)); 43 | when(httpExchange.getRequestMethod()).thenReturn("DELETE"); 44 | handler.handle(httpExchange); 45 | assertThat(requestHandler.receivedParam, is(PARAM_ID)); 46 | 47 | } 48 | 49 | private class StubRequestHandler implements RequestHandler { 50 | public String receivedParam; 51 | 52 | @Override 53 | public Object handleRequest(BlinkRequest request, BlinkResponse response) { 54 | receivedParam = request.pathParam("id"); 55 | return ""; 56 | } 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /src/test/resources/blink-img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rockem/blink-java/d1db45daa9b72a67d6ba02bfdeef48693f5a0365/src/test/resources/blink-img.jpg --------------------------------------------------------------------------------