Skip to content

Commit 01467a0

Browse files
committed
ssl support without cert - closes #6, implemented accept header, better error reporting for empty request - fixes #5
1 parent 7f40df1 commit 01467a0

File tree

8 files changed

+122
-19
lines changed

8 files changed

+122
-19
lines changed

README.md

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ And you don't need to create Java objects (or POJO-s) for any of the payloads th
4444
**Variables & Expressions** | [`def`](#def) | [`assert`](#assert) | [`print`](#print) | [Multi-line](#multi-line-expressions)
4545
**Data Types** | [JSON](#json) | [XML](#xml) | [JS Functions](#javascript-functions) | [Reading Files](#reading-files)
4646
**Primary HTTP Keywords** | [`url`](#url) | [`path`](#path) | [`request`](#request) | [`method`](#method)
47-
| [`status`](#status) | [`multipart post`](#multipart-post) | [`soap action`](#soap-action)
47+
| [`status`](#status) | [`accept`](#accept) | [`multipart post`](#multipart-post) | [`soap action`](#soap-action)
4848
**Secondary HTTP Keywords** | [`param`](#param) | [`header`](#header) | [`cookie`](#cookie)
4949
| [`form field`](#form-field) | [`multipart field`](#multipart-field) | [`multipart entity`](#multipart-entity)
5050
**Set, Match, Assert** | [`set`](#set) | [`match`](#match) | [`contains`](#match-contains) | [Ignore / Vallidate](#ignore-or-validate)
@@ -63,6 +63,7 @@ And you don't need to create Java objects (or POJO-s) for any of the payloads th
6363
* Invoke and re-use existing Java code if you need to
6464
* Built-in support for switching configuration across different environments (e.g. dev, QA, pre-prod)
6565
* Simple plug-in system for handling authentication and setting up HTTP headers
66+
* Support for data-driven tests and being able to tag (or group) tests is built-in, so you don’t have to rely on TestNG or JUnit for those features any more
6667
* Comprehensive support for different flavors of HTTP calls
6768
* SOAP / XML requests
6869
* URL-encoded HTML-form submits
@@ -159,7 +160,7 @@ This is all that you need within your `<dependencies>`:
159160
<dependency>
160161
<groupId>com.intuit.karate</groupId>
161162
<artifactId>karate-core</artifactId>
162-
<version>0.1.3</version>
163+
<version>0.1.4</version>
163164
<scope>test</scope>
164165
</dependency>
165166
```
@@ -175,7 +176,7 @@ You can replace the values of 'com.mycompany' and 'myproject' as per your needs.
175176
mvn archetype:generate \
176177
-DarchetypeGroupId=com.intuit.karate \
177178
-DarchetypeArtifactId=karate-archetype \
178-
-DarchetypeVersion=0.1.3 \
179+
-DarchetypeVersion=0.1.4 \
179180
-DgroupId=com.mycompany \
180181
-DartifactId=myproject
181182
```
@@ -768,6 +769,18 @@ Then status 200
768769
```
769770
And this assertion will cause the test to fail if the HTTP response code is something else.
770771

772+
### `accept`
773+
By default, Karate tries to determine the right `Accept` HTTP header value from the request payload
774+
data type (JSON / XML / String / Stream).
775+
776+
You can easily set it explicitly as follows:
777+
```cucumber
778+
Given request '<html></html>'
779+
And accept 'text/html'
780+
When method post
781+
Then status 200
782+
```
783+
771784
See also [`responseStatus`](#responsestatus).
772785

773786
## Keywords that set key-value pairs
@@ -956,6 +969,8 @@ Marker | Description
956969
#ignore | Skip comparison for this field
957970
#null | Expects actual value to be null
958971
#notnull | Expects actual value to be not-null
972+
#array | Expects actual value to be a JSON array
973+
#object | Expects actual value to be a JSON object
959974
#boolean | Expects actual value to be a boolean `true` or `false`
960975
#number | Expects actual value to be a number
961976
#string | Expects actual value to be a string
@@ -1281,6 +1296,25 @@ In fact, this is the mechanism used when [`karate-config.js`](#configuration) is
12811296
You can invoke a function in a [re-usable file](#reading-files) using this short-cut.
12821297
```cucumber
12831298
* call read('my-function.js')
1299+
```
1300+
### HTTP Basic Authentication
1301+
This should make it clear why Karate does not actually come with support for any authentication scheme
1302+
built-in. Things are designed so that you can plug-in what you need, without needing to compile Java code.
1303+
1304+
First the JavaScript file, `basic-auth.js`:
1305+
```javascript
1306+
function(creds) {
1307+
var temp = creds.username + ':' + creds.password;
1308+
var Base64 = Java.type("java.util.Base64");
1309+
var encoded = Base64.getEncoder().encodeToString(temp.bytes);
1310+
return 'Basic ' + encoded;
1311+
}
1312+
```
1313+
And here how it works in a test-script. Note that you need to do this only once within a `Scenario:`,
1314+
perhaps at the beginning.
1315+
```cucumber
1316+
* header Authorization = call read('basic-auth.js') { username: 'john', password: 'secret' }
1317+
12841318
```
12851319

12861320
## Calling Java

karate-archetype/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>com.intuit.karate</groupId>
77
<artifactId>karate-parent</artifactId>
8-
<version>0.1.3</version>
8+
<version>0.1.4</version>
99
</parent>
1010
<artifactId>karate-archetype</artifactId>
1111
<packaging>jar</packaging>

karate-archetype/src/main/resources/archetype-resources/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<dependency>
1818
<groupId>com.intuit.karate</groupId>
1919
<artifactId>karate-core</artifactId>
20-
<version>0.1.3</version>
20+
<version>0.1.4</version>
2121
<scope>test</scope>
2222
</dependency>
2323
</dependencies>

karate-core/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>com.intuit.karate</groupId>
77
<artifactId>karate-parent</artifactId>
8-
<version>0.1.3</version>
8+
<version>0.1.4</version>
99
</parent>
1010
<artifactId>karate-core</artifactId>
1111
<packaging>jar</packaging>

karate-core/src/main/java/com/intuit/karate/ScriptContext.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.intuit.karate.validator.Validator;
44
import java.util.Map;
55
import java.util.logging.Level;
6+
import javax.net.ssl.SSLContext;
67
import javax.ws.rs.client.Client;
78
import javax.ws.rs.client.ClientBuilder;
89
import jdk.nashorn.api.scripting.ScriptObjectMirror;
@@ -27,11 +28,13 @@ public class ScriptContext {
2728
protected final String featureDir;
2829
protected final ClassLoader fileClassLoader;
2930
protected final String env;
31+
protected final ClientBuilder clientBuilder;
3032

33+
// needed for 3rd party code
3134
public ScriptValueMap getVars() {
3235
return vars;
3336
}
34-
37+
3538
public ScriptContext(boolean test, String featureDir, ClassLoader fileClassLoader, String env) {
3639
this.featureDir = featureDir;
3740
this.fileClassLoader = fileClassLoader;
@@ -43,17 +46,21 @@ public ScriptContext(boolean test, String featureDir, ClassLoader fileClassLoade
4346
if (test) {
4447
logger.trace("karate init in test mode, http client disabled");
4548
client = null;
49+
clientBuilder = null;
4650
return;
4751
}
48-
ClientBuilder cb = ClientBuilder.newBuilder()
52+
clientBuilder = ClientBuilder.newBuilder()
4953
.register(MultiPartFeature.class);
5054
if (logger.isDebugEnabled()) {
51-
cb.register(new LoggingFeature(
55+
clientBuilder.register(new LoggingFeature(
5256
java.util.logging.Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME),
5357
Level.SEVERE,
5458
LoggingFeature.Verbosity.PAYLOAD_TEXT, null));
55-
}
56-
client = cb.build();
59+
}
60+
SSLContext sslContext = SslUtils.getSslContext();
61+
clientBuilder.sslContext(sslContext);
62+
clientBuilder.hostnameVerifier((host, session) -> true);
63+
client = clientBuilder.build();
5764
client.register(new RequestFilter(this));
5865
// auto config
5966
try {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.intuit.karate;
2+
3+
import java.security.SecureRandom;
4+
import java.security.cert.X509Certificate;
5+
import javax.net.ssl.TrustManager;
6+
import javax.net.ssl.X509TrustManager;
7+
import java.security.cert.CertificateException;
8+
import javax.net.ssl.HttpsURLConnection;
9+
import javax.net.ssl.SSLContext;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
13+
/**
14+
*
15+
* @author pthomas3
16+
*/
17+
public class SslUtils {
18+
19+
private static final Logger logger = LoggerFactory.getLogger(SslUtils.class);
20+
21+
private SslUtils() {
22+
// only static methods
23+
}
24+
25+
public static SSLContext getSslContext() {
26+
TrustManager[] certs = new TrustManager[]{new X509TrustManager() {
27+
@Override
28+
public X509Certificate[] getAcceptedIssuers() {
29+
logger.trace("get accepted issuers");
30+
return new X509Certificate[0];
31+
}
32+
@Override
33+
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
34+
logger.trace("check server trusted");
35+
}
36+
@Override
37+
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
38+
logger.trace("check client trusted");
39+
}
40+
}};
41+
SSLContext ctx = null;
42+
try {
43+
ctx = SSLContext.getInstance("TLS");
44+
ctx.init(null, certs, new SecureRandom());
45+
} catch (Exception e) {
46+
throw new RuntimeException(e);
47+
}
48+
HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());
49+
return ctx;
50+
}
51+
52+
}

karate-core/src/main/java/com/intuit/karate/StepDefs.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,15 @@ public StepDefs(String featureDir, ClassLoader fileClassLoader, String env) {
4747
private Response response;
4848
private String accept;
4949
private long startTime;
50-
50+
5151
private final ScriptContext context;
5252

5353
public ScriptContext getContext() {
5454
return context;
55-
}
55+
}
5656

5757
@When("^url (.+)")
58-
public void url(String expression) {
58+
public void url(String expression) {
5959
String temp = Script.preEval(expression, context).getAsString();
6060
this.url = temp;
6161
target = context.client.target(temp);
@@ -159,7 +159,7 @@ public void asssertBoolean(String expression) {
159159
handleFailure(ar);
160160
}
161161

162-
private Invocation.Builder prepare() {
162+
private Invocation.Builder prepare() {
163163
hasUrlBeenSet();
164164
Invocation.Builder builder = target.request();
165165
Map<String, Object> headers = context.vars.get(VAR_HEADERS, Map.class);
@@ -181,8 +181,8 @@ private Invocation.Builder prepare() {
181181
}
182182

183183
@When("^accept (.+)")
184-
public void accept(String expression) {
185-
this.accept = expression;
184+
public void accept(String exp) {
185+
this.accept = Script.preEval(exp, context).getAsString();
186186
}
187187

188188
private void startTimer() {
@@ -207,6 +207,11 @@ public void method(String method) {
207207
stopTimer();
208208
} else {
209209
ScriptValue sv = context.vars.get(VAR_REQUEST);
210+
if (sv == null || sv.isNull()) {
211+
String msg = "request body is requred for a " + method + ", please use the 'request' keyword";
212+
logger.error(msg);
213+
throw new RuntimeException(msg);
214+
}
210215
Entity entity;
211216
switch (sv.getType()) {
212217
case JSON:
@@ -253,7 +258,12 @@ private void unprepare() {
253258
if (Script.isJson(responseBody)) {
254259
context.vars.put(ScriptValueMap.VAR_RESPONSE, JsonUtils.toJsonDoc(responseBody));
255260
} else if (Script.isXml(responseBody)) {
256-
context.vars.put(ScriptValueMap.VAR_RESPONSE, XmlUtils.toXmlDoc(responseBody));
261+
try {
262+
context.vars.put(ScriptValueMap.VAR_RESPONSE, XmlUtils.toXmlDoc(responseBody));
263+
} catch (Exception e) {
264+
logger.warn("xml parsing failed, response data type set to string: {}", e.getMessage());
265+
context.vars.put(ScriptValueMap.VAR_RESPONSE, responseBody);
266+
}
257267
} else {
258268
context.vars.put(ScriptValueMap.VAR_RESPONSE, responseBody);
259269
}

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>com.intuit.karate</groupId>
66
<artifactId>karate-parent</artifactId>
7-
<version>0.1.3</version>
7+
<version>0.1.4</version>
88
<packaging>pom</packaging>
99

1010
<name>${project.artifactId}</name>

0 commit comments

Comments
 (0)