aboutsummaryrefslogtreecommitdiffstats
path: root/clustercontroller-utils/src/main/java/com/yahoo/vespa/clustercontroller/utils/communication/http/HttpRequest.java
blob: 939c287b9cedc2a57781073919eef6795a9a1ef9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.clustercontroller.utils.communication.http;

import com.yahoo.vespa.clustercontroller.utils.util.CertainlyCloneable;

import java.util.LinkedList;
import java.util.List;

public class HttpRequest extends CertainlyCloneable<HttpRequest> {

    public static class KeyValuePair {
        public final String key;
        public final String value;

        public KeyValuePair(String k, String v) { this.key = k; this.value = v; }

        public String getValue() {
            return value;
        }

        public String getKey() {
            return key;
        }
    }

    public enum HttpOp { GET, POST, PUT, DELETE }

    private String scheme;
    private String host;
    private int port;
    private String path;
    private List<KeyValuePair> urlOptions = new LinkedList<>();
    private List<KeyValuePair> headers = new LinkedList<>();
    private long timeoutMillis;
    private Object postContent;
    private HttpOp httpOperation;

    public HttpRequest() {}

    public String getScheme() { return scheme; }
    public String getHost() { return host; }
    public int getPort() { return port; }
    public String getPath() { return path; }
    public List<KeyValuePair> getUrlOptions() { return urlOptions; }
    public String getOption(String key, String defaultValue) {
        for (KeyValuePair value : urlOptions) {
            if (value.key.equals(key)) return value.value;
        }
        return defaultValue;

    }
    public List<KeyValuePair> getHeaders() { return headers; }
    public String getHeader(String key, String defaultValue) {
        for (KeyValuePair header : headers) {
            if (header.key.equals(key)) return header.value;
        }
        return defaultValue;
    }
    public Object getPostContent() { return postContent; }
    public HttpOp getHttpOperation() { return httpOperation; }

    public HttpRequest setScheme(String scheme) { this.scheme = scheme; return this; }
    public HttpRequest setHost(String hostname) { this.host = hostname; return this; }
    public HttpRequest setPort(int port) { this.port = port; return this; }
    public HttpRequest setPath(String path) {
        this.path = path; return this;
    }
    public HttpRequest addUrlOption(String key, String value) { this.urlOptions.add(new KeyValuePair(key, value)); return this; }
    public HttpRequest setUrlOptions(List<KeyValuePair> options) { this.urlOptions.clear(); this.urlOptions.addAll(options); return this; }
    public HttpRequest addHttpHeader(String key, String value) { this.headers.add(new KeyValuePair(key, value)); return this; }
    public HttpRequest setTimeout(long timeoutMillis) { this.timeoutMillis = timeoutMillis; return this; }
    public HttpRequest setPostContent(Object content) { this.postContent = content; return this; }
    public HttpRequest setHttpOperation(HttpOp op) { this.httpOperation = op; return this; }

    /** Create a copy of this request, and override what is specified in the input in the new request. */
    public HttpRequest merge(HttpRequest r) {
        HttpRequest copy = clone();
        if (r.scheme != null) copy.scheme = r.scheme;
        if (r.host != null) copy.host = r.host;
        if (r.port != 0) copy.port = r.port;
        if (r.path != null) copy.path = r.path;
        for (KeyValuePair h : r.headers) {
            boolean containsElement = false;
            for (KeyValuePair h2 : copy.headers) { containsElement |= (h.key.equals(h2.key)); }
            if (!containsElement) copy.headers.add(h);
        }
        for (KeyValuePair h : r.urlOptions) {
            boolean containsElement = false;
            for (KeyValuePair h2 : copy.urlOptions) { containsElement |= (h.key.equals(h2.key)); }
            if (!containsElement) copy.urlOptions.add(h);
        }
        if (r.timeoutMillis != 0) copy.timeoutMillis = r.timeoutMillis;
        if (r.postContent != null) copy.postContent = r.postContent;
        if (r.httpOperation != null) copy.httpOperation = r.httpOperation;
        return copy;
    }

    @Override
    public HttpRequest clone() {
        HttpRequest r = (HttpRequest) super.clone();
        r.headers = new LinkedList<>(r.headers);
        r.urlOptions = new LinkedList<>(r.urlOptions);
        return r;
    }

    @Override
    public String toString() { return toString(false); }
    public String toString(boolean verbose) {
        String httpOp = (httpOperation != null ? httpOperation.toString()
                                               : (postContent == null ? "GET?" : "POST?"));
        StringBuilder sb = new StringBuilder().append(httpOp).append(" ").append(scheme).append(':');
        if (host != null) {
            sb.append("//").append(host);
            if (port != 0) sb.append(':').append(port);
        }
        if (path == null || path.isEmpty()) {
            sb.append('/');
        } else {
            if (path.charAt(0) != '/') sb.append('/');
            sb.append(path);
        }
        if (urlOptions != null && urlOptions.size() > 0) {
            boolean first = (path == null || path.indexOf('?') < 0);
            for (KeyValuePair e : urlOptions) {
                sb.append(first ? '?' : '&');
                first = false;
                sb.append(e.key).append('=').append(e.value);
            }
        }
        if (verbose) {
            for (KeyValuePair p : headers) {
                sb.append('\n').append(p.key).append(": ").append(p.value);
            }
            if (postContent != null && !postContent.toString().isEmpty()) {
                sb.append("\n\n").append(postContent.toString());
            }
        }
        return sb.toString();
    }

    public void verifyComplete() {
        if (path == null) throw new IllegalStateException("HTTP requests must have a path set. Use '/' for top level");
        if (httpOperation == null) throw new IllegalStateException("HTTP requests must have an HTTP method defined");
    }

    public boolean equals(Object o) {
        // Equals is only used for debugging as far as we know. Refer to verbose toString to simplify
        if (o instanceof HttpRequest) {
            return toString(true).equals(((HttpRequest) o).toString(true));
        }
        return false;
    }

    public int hashCode() {
        // not used, only here to match equals() and avoid lint warning
        return toString(true).hashCode();
    }
}