1 /* 2 * Copyright (C) 2013 Square, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.squareup.okhttp; 17 18 import com.squareup.okhttp.internal.Util; 19 import com.squareup.okhttp.internal.http.RawHeaders; 20 import java.io.File; 21 import java.io.FileInputStream; 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.io.OutputStream; 25 import java.io.UnsupportedEncodingException; 26 import java.net.MalformedURLException; 27 import java.net.URL; 28 import java.util.List; 29 import java.util.Set; 30 31 /** 32 * An HTTP request. Instances of this class are immutable if their {@link #body} 33 * is null or itself immutable. 34 * 35 * <h3>Warning: Experimental OkHttp 2.0 API</h3> 36 * This class is in beta. APIs are subject to change! 37 */ 38 public final class Request { 39 private final URL url; 40 private final String method; 41 private final RawHeaders headers; 42 private final Body body; 43 private final Object tag; 44 45 private Request(Builder builder) { 46 this.url = builder.url; 47 this.method = builder.method; 48 this.headers = new RawHeaders(builder.headers); 49 this.body = builder.body; 50 this.tag = builder.tag != null ? builder.tag : this; 51 } 52 53 public URL url() { 54 return url; 55 } 56 57 public String urlString() { 58 return url.toString(); 59 } 60 61 public String method() { 62 return method; 63 } 64 65 public String header(String name) { 66 return headers.get(name); 67 } 68 69 public List<String> headers(String name) { 70 return headers.values(name); 71 } 72 73 public Set<String> headerNames() { 74 return headers.names(); 75 } 76 77 public int headerCount() { 78 return headers.length(); 79 } 80 81 public String headerName(int index) { 82 return headers.getFieldName(index); 83 } 84 85 public String headerValue(int index) { 86 return headers.getValue(index); 87 } 88 89 public Body body() { 90 return body; 91 } 92 93 public Object tag() { 94 return tag; 95 } 96 97 public abstract static class Body { 98 /** 99 * Returns the Content-Type header for this body, or null if the content 100 * type is unknown. 101 */ 102 public MediaType contentType() { 103 return null; 104 } 105 106 /** Returns the number of bytes in this body, or -1 if that count is unknown. */ 107 public long contentLength() { 108 return -1; 109 } 110 111 /** Writes the content of this request to {@code out}. */ 112 public abstract void writeTo(OutputStream out) throws IOException; 113 114 /** 115 * Returns a new request body that transmits {@code content}. If {@code 116 * contentType} lacks a charset, this will use UTF-8. 117 */ 118 public static Body create(MediaType contentType, String content) { 119 contentType = contentType.charset() != null 120 ? contentType 121 : MediaType.parse(contentType + "; charset=utf-8"); 122 try { 123 byte[] bytes = content.getBytes(contentType.charset().name()); 124 return create(contentType, bytes); 125 } catch (UnsupportedEncodingException e) { 126 throw new AssertionError(); 127 } 128 } 129 130 /** Returns a new request body that transmits {@code content}. */ 131 public static Body create(final MediaType contentType, final byte[] content) { 132 if (contentType == null) throw new NullPointerException("contentType == null"); 133 if (content == null) throw new NullPointerException("content == null"); 134 135 return new Body() { 136 @Override public MediaType contentType() { 137 return contentType; 138 } 139 140 @Override public long contentLength() { 141 return content.length; 142 } 143 144 @Override public void writeTo(OutputStream out) throws IOException { 145 out.write(content); 146 } 147 }; 148 } 149 150 /** Returns a new request body that transmits the content of {@code file}. */ 151 public static Body create(final MediaType contentType, final File file) { 152 if (contentType == null) throw new NullPointerException("contentType == null"); 153 if (file == null) throw new NullPointerException("content == null"); 154 155 return new Body() { 156 @Override public MediaType contentType() { 157 return contentType; 158 } 159 160 @Override public long contentLength() { 161 return file.length(); 162 } 163 164 @Override public void writeTo(OutputStream out) throws IOException { 165 long length = contentLength(); 166 if (length == 0) return; 167 168 InputStream in = null; 169 try { 170 in = new FileInputStream(file); 171 byte[] buffer = new byte[(int) Math.min(8192, length)]; 172 for (int c; (c = in.read(buffer)) != -1; ) { 173 out.write(buffer, 0, c); 174 } 175 } finally { 176 Util.closeQuietly(in); 177 } 178 } 179 }; 180 } 181 } 182 183 public static class Builder { 184 private URL url; 185 private String method = "GET"; 186 private final RawHeaders headers = new RawHeaders(); 187 private Body body; 188 private Object tag; 189 190 public Builder(String url) { 191 url(url); 192 } 193 194 public Builder(URL url) { 195 url(url); 196 } 197 198 public Builder url(String url) { 199 try { 200 this.url = new URL(url); 201 return this; 202 } catch (MalformedURLException e) { 203 throw new IllegalArgumentException("Malformed URL: " + url); 204 } 205 } 206 207 public Builder url(URL url) { 208 if (url == null) throw new IllegalStateException("url == null"); 209 this.url = url; 210 return this; 211 } 212 213 /** 214 * Sets the header named {@code name} to {@code value}. If this request 215 * already has any headers with that name, they are all replaced. 216 */ 217 public Builder header(String name, String value) { 218 headers.set(name, value); 219 return this; 220 } 221 222 /** 223 * Adds a header with {@code name} and {@code value}. Prefer this method for 224 * multiply-valued headers like "Cookie". 225 */ 226 public Builder addHeader(String name, String value) { 227 headers.add(name, value); 228 return this; 229 } 230 231 public Builder get() { 232 return method("GET", null); 233 } 234 235 public Builder head() { 236 return method("HEAD", null); 237 } 238 239 public Builder post(Body body) { 240 return method("POST", body); 241 } 242 243 public Builder put(Body body) { 244 return method("PUT", body); 245 } 246 247 public Builder method(String method, Body body) { 248 if (method == null || method.length() == 0) { 249 throw new IllegalArgumentException("method == null || method.length() == 0"); 250 } 251 this.method = method; 252 this.body = body; 253 return this; 254 } 255 256 /** 257 * Attaches {@code tag} to the request. It can be used later to cancel the 258 * request. If the tag is unspecified or null, the request is canceled by 259 * using the request itself as the tag. 260 */ 261 public Builder tag(Object tag) { 262 this.tag = tag; 263 return this; 264 } 265 266 public Request build() { 267 return new Request(this); 268 } 269 } 270 } 271