Skip to content

Commit 7546ec7

Browse files
authored
Merge pull request #113 from thc202/post-method
Allow to call the API with custom HTTP method
2 parents efd5026 + 72fe7ef commit 7546ec7

File tree

2 files changed

+88
-15
lines changed

2 files changed

+88
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
## [Unreleased]
88
### Changed
99
- Stop sending the API key as query parameter, not needed since ZAP 2.6.0.
10+
- Allow to call the ZAP API with custom HTTP method (e.g. file upload).
1011

1112
### Deprecated
1213
- The following APIs were deprecated:

subprojects/zap-clientapi/src/main/java/org/zaproxy/clientapi/core/ClientApi.java

Lines changed: 87 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.io.IOException;
2626
import java.io.InputStream;
2727
import java.io.InputStreamReader;
28+
import java.io.OutputStreamWriter;
2829
import java.io.PrintStream;
2930
import java.io.UnsupportedEncodingException;
3031
import java.net.HttpURLConnection;
@@ -374,15 +375,30 @@ private static URL createUrl(String value) throws MalformedURLException, URISynt
374375
public ApiResponse callApi(
375376
String component, String type, String method, Map<String, String> params)
376377
throws ClientApiException {
377-
Document dom = this.callApiDom(component, type, method, params);
378+
return callApi(HttpRequest.GET_METHOD, component, type, method, params);
379+
}
380+
381+
public ApiResponse callApi(
382+
String requestMethod,
383+
String component,
384+
String type,
385+
String method,
386+
Map<String, String> params)
387+
throws ClientApiException {
388+
Document dom = this.callApiDom(requestMethod, component, type, method, params);
378389
return ApiResponseFactory.getResponse(dom.getFirstChild());
379390
}
380391

381392
private Document callApiDom(
382-
String component, String type, String method, Map<String, String> params)
393+
String requestMethod,
394+
String component,
395+
String type,
396+
String method,
397+
Map<String, String> params)
383398
throws ClientApiException {
384399
try {
385-
HttpRequest request = buildZapRequest("xml", component, type, method, params);
400+
HttpRequest request =
401+
buildZapRequest(requestMethod, "xml", component, type, method, params);
386402
if (debug) {
387403
debugStream.println("Open URL: " + request.getRequestUri());
388404
}
@@ -422,6 +438,17 @@ private InputStream getConnectionInputStream(HttpRequest request) throws IOExcep
422438
for (Entry<String, String> header : request.getHeaders().entrySet()) {
423439
uc.setRequestProperty(header.getKey(), header.getValue());
424440
}
441+
if (!isGetRequest(request.getMethod())) {
442+
uc.setRequestMethod(request.getMethod());
443+
String body = request.getBody();
444+
if (body != null && !body.isEmpty()) {
445+
uc.setDoOutput(true);
446+
try (var os =
447+
new OutputStreamWriter(uc.getOutputStream(), StandardCharsets.UTF_8)) {
448+
os.write(request.getBody());
449+
}
450+
}
451+
}
425452
uc.connect();
426453
if (uc.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) {
427454
return uc.getErrorStream();
@@ -432,8 +459,19 @@ private InputStream getConnectionInputStream(HttpRequest request) throws IOExcep
432459
public byte[] callApiOther(
433460
String component, String type, String method, Map<String, String> params)
434461
throws ClientApiException {
462+
return callApiOther(HttpRequest.GET_METHOD, component, type, method, params);
463+
}
464+
465+
public byte[] callApiOther(
466+
String requestMethod,
467+
String component,
468+
String type,
469+
String method,
470+
Map<String, String> params)
471+
throws ClientApiException {
435472
try {
436-
HttpRequest request = buildZapRequest("other", component, type, method, params);
473+
HttpRequest request =
474+
buildZapRequest(requestMethod, "other", component, type, method, params);
437475
if (debug) {
438476
debugStream.println("Open URL: " + request.getRequestUri());
439477
}
@@ -462,6 +500,7 @@ public byte[] callApiOther(
462500
* <p>As the API client proxies through ZAP the built API requests use a specific domain, {@code
463501
* zap}, to ensure that they are always handled by ZAP (and not forward).
464502
*
503+
* @param requestMethod the HTTP request method.
465504
* @param format the desired format of the API response (e.g. XML, JSON, other).
466505
* @param component the API component (e.g. core, spider).
467506
* @param type the type of the API endpoint (e.g. action, view).
@@ -472,7 +511,12 @@ public byte[] callApiOther(
472511
* @throws URISyntaxException if an error occurred while building the URL.
473512
*/
474513
private HttpRequest buildZapRequest(
475-
String format, String component, String type, String method, Map<String, String> params)
514+
String requestMethod,
515+
String format,
516+
String component,
517+
String type,
518+
String method,
519+
Map<String, String> params)
476520
throws MalformedURLException, URISyntaxException {
477521
StringBuilder sb = new StringBuilder();
478522
sb.append("http://zap/");
@@ -484,25 +528,39 @@ private HttpRequest buildZapRequest(
484528
sb.append('/');
485529
sb.append(method);
486530
sb.append('/');
531+
String body = null;
487532
if (params != null) {
488-
sb.append('?');
489-
for (Map.Entry<String, String> p : params.entrySet()) {
490-
sb.append(encodeQueryParam(p.getKey()));
491-
sb.append('=');
492-
if (p.getValue() != null) {
493-
sb.append(encodeQueryParam(p.getValue()));
494-
}
495-
sb.append('&');
533+
if (isGetRequest(requestMethod)) {
534+
sb.append('?');
535+
appendParams(params, sb);
536+
} else {
537+
body = appendParams(params, new StringBuilder()).toString();
496538
}
497539
}
498540

499-
HttpRequest request = new HttpRequest(createUrl(sb.toString()));
541+
HttpRequest request = new HttpRequest(requestMethod, createUrl(sb.toString()), body);
500542
if (apiKey != null && !apiKey.isEmpty()) {
501543
request.addHeader(ZAP_API_KEY_HEADER, apiKey);
502544
}
503545
return request;
504546
}
505547

548+
private static boolean isGetRequest(String requestMethod) {
549+
return HttpRequest.GET_METHOD.equals(requestMethod);
550+
}
551+
552+
private static StringBuilder appendParams(Map<String, String> params, StringBuilder sb) {
553+
for (Map.Entry<String, String> p : params.entrySet()) {
554+
sb.append(encodeQueryParam(p.getKey()));
555+
sb.append('=');
556+
if (p.getValue() != null) {
557+
sb.append(encodeQueryParam(p.getValue()));
558+
}
559+
sb.append('&');
560+
}
561+
return sb;
562+
}
563+
506564
private static String encodeQueryParam(String param) {
507565
try {
508566
return URLEncoder.encode(param, "UTF-8");
@@ -748,12 +806,22 @@ private static ClientApiException newTimeoutConnectionToZap(int timeoutInSeconds
748806
*/
749807
private static class HttpRequest {
750808

809+
private static final String GET_METHOD = "GET";
810+
811+
private final String method;
751812
private final URL requestUri;
752813
private final Map<String, String> headers;
814+
private final String body;
753815

754-
public HttpRequest(URL url) {
816+
public HttpRequest(String method, URL url, String body) {
817+
this.method = method;
755818
this.requestUri = url;
756819
this.headers = new HashMap<>();
820+
this.body = body;
821+
}
822+
823+
public String getMethod() {
824+
return method;
757825
}
758826

759827
/**
@@ -786,5 +854,9 @@ public void addHeader(String name, String value) {
786854
public Map<String, String> getHeaders() {
787855
return Collections.unmodifiableMap(headers);
788856
}
857+
858+
public String getBody() {
859+
return body;
860+
}
789861
}
790862
}

0 commit comments

Comments
 (0)