1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 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 17 package com.badlogic.gdx; 18 19 import java.io.InputStream; 20 import java.io.OutputStream; 21 import java.util.HashMap; 22 import java.util.List; 23 import java.util.Map; 24 25 import com.badlogic.gdx.Application.ApplicationType; 26 import com.badlogic.gdx.net.HttpRequestHeader; 27 import com.badlogic.gdx.net.HttpResponseHeader; 28 import com.badlogic.gdx.net.HttpStatus; 29 import com.badlogic.gdx.net.ServerSocket; 30 import com.badlogic.gdx.net.ServerSocketHints; 31 import com.badlogic.gdx.net.Socket; 32 import com.badlogic.gdx.net.SocketHints; 33 import com.badlogic.gdx.utils.GdxRuntimeException; 34 import com.badlogic.gdx.utils.Pool.Poolable; 35 36 /** Provides methods to perform networking operations, such as simple HTTP get and post requests, and TCP server/client socket 37 * communication.</p> 38 * 39 * To perform an HTTP request create a {@link HttpRequest} with the HTTP method (see {@link HttpMethods} for common methods) and 40 * invoke {@link #sendHttpRequest(HttpRequest, HttpResponseListener)} with it and a {@link HttpResponseListener}. After the HTTP 41 * request was processed, the {@link HttpResponseListener} is called with a {@link HttpResponse} with the HTTP response values and 42 * an status code to determine if the request was successful or not.</p> 43 * 44 * To create a TCP client socket to communicate with a remote TCP server, invoke the 45 * {@link #newClientSocket(Protocol, String, int, SocketHints)} method. The returned {@link Socket} offers an {@link InputStream} 46 * and {@link OutputStream} to communicate with the end point.</p> 47 * 48 * To create a TCP server socket that waits for incoming connections, invoke the 49 * {@link #newServerSocket(Protocol, int, ServerSocketHints)} method. The returned {@link ServerSocket} offers an 50 * {@link ServerSocket#accept(SocketHints options)} method that waits for an incoming connection. 51 * 52 * @author mzechner 53 * @author noblemaster 54 * @author arielsan */ 55 public interface Net { 56 57 /** HTTP response interface with methods to get the response data as a byte[], a {@link String} or an {@link InputStream}. */ 58 public static interface HttpResponse { 59 /** Returns the data of the HTTP response as a byte[]. 60 * <p> 61 * <b>Note</b>: This method may only be called once per response. 62 * </p> 63 * @return the result as a byte[] or null in case of a timeout or if the operation was canceled/terminated abnormally. The 64 * timeout is specified when creating the HTTP request, with {@link HttpRequest#setTimeOut(int)} */ getResult()65 byte[] getResult (); 66 67 /** Returns the data of the HTTP response as a {@link String}. 68 * <p> 69 * <b>Note</b>: This method may only be called once per response. 70 * </p> 71 * @return the result as a string or null in case of a timeout or if the operation was canceled/terminated abnormally. The 72 * timeout is specified when creating the HTTP request, with {@link HttpRequest#setTimeOut(int)} */ getResultAsString()73 String getResultAsString (); 74 75 /** Returns the data of the HTTP response as an {@link InputStream}. <b><br> 76 * Warning:</b> Do not store a reference to this InputStream outside of 77 * {@link HttpResponseListener#handleHttpResponse(HttpResponse)}. The underlying HTTP connection will be closed after that 78 * callback finishes executing. Reading from the InputStream after it's connection has been closed will lead to exception. 79 * @return An {@link InputStream} with the {@link HttpResponse} data. */ getResultAsStream()80 InputStream getResultAsStream (); 81 82 /** Returns the {@link HttpStatus} containing the statusCode of the HTTP response. */ getStatus()83 HttpStatus getStatus (); 84 85 /** Returns the value of the header with the given name as a {@link String}, or null if the header is not set. See 86 * {@link HttpResponseHeader}. */ getHeader(String name)87 String getHeader (String name); 88 89 /** Returns a Map of the headers. The keys are Strings that represent the header name. Each values is a List of Strings that 90 * represent the corresponding header values. See {@link HttpResponseHeader}. */ getHeaders()91 Map<String, List<String>> getHeaders (); 92 } 93 94 /** Provides common HTTP methods to use when creating a {@link HttpRequest}. 95 * <ul> 96 * <li>GET</li> 97 * <li>POST</li> 98 * <li>PUT</li> 99 * <li>DELETE</li> 100 * </ul> */ 101 public static interface HttpMethods { 102 103 public static final String GET = "GET"; 104 public static final String POST = "POST"; 105 public static final String PUT = "PUT"; 106 public static final String DELETE = "DELETE"; 107 108 } 109 110 /** Contains getters and setters for the following parameters: 111 * <ul> 112 * <li><strong>httpMethod:</strong> GET or POST are most common, can use {@link Net.HttpMethods HttpMethods} for static 113 * references</li> 114 * <li><strong>url:</strong> the url</li> 115 * <li><strong>headers:</strong> a map of the headers, setter can be called multiple times</li> 116 * <li><strong>timeout:</strong> time spent trying to connect before giving up</li> 117 * <li><strong>content:</strong> A string containing the data to be used when processing the HTTP request.</li> 118 * </ul> 119 * 120 * Abstracts the concept of a HTTP Request: 121 * 122 * <pre> 123 * Map<String, String> parameters = new HashMap<String, String>(); 124 * parameters.put("user", "myuser"); 125 * 126 * HttpRequest httpGet = new HttpRequest(HttpMethods.Get); 127 * httpGet.setUrl("http://somewhere.net"); 128 * httpGet.setContent(HttpParametersUtils.convertHttpParameters(parameters)); 129 * ... 130 * Gdx.net.sendHttpRequest (httpGet, new HttpResponseListener() { 131 * public void handleHttpResponse(HttpResponse httpResponse) { 132 * status = httpResponse.getResultAsString(); 133 * //do stuff here based on response 134 * } 135 * 136 * public void failed(Throwable t) { 137 * status = "failed"; 138 * //do stuff here based on the failed attempt 139 * } 140 * }); 141 * </pre> */ 142 public static class HttpRequest implements Poolable { 143 144 private String httpMethod; 145 private String url; 146 private Map<String, String> headers; 147 private int timeOut = 0; 148 149 private String content; 150 private InputStream contentStream; 151 private long contentLength; 152 153 private boolean followRedirects = true; 154 155 private boolean includeCredentials = false; 156 HttpRequest()157 public HttpRequest () { 158 this.headers = new HashMap<String, String>(); 159 } 160 161 /** Creates a new HTTP request with the specified HTTP method, see {@link HttpMethods}. 162 * @param httpMethod This is the HTTP method for the request, see {@link HttpMethods} */ HttpRequest(String httpMethod)163 public HttpRequest (String httpMethod) { 164 this(); 165 this.httpMethod = httpMethod; 166 } 167 168 /** Sets the URL of the HTTP request. 169 * @param url The URL to set. */ setUrl(String url)170 public void setUrl (String url) { 171 this.url = url; 172 } 173 174 /** Sets a header to this HTTP request, see {@link HttpRequestHeader}. 175 * @param name the name of the header. 176 * @param value the value of the header. */ setHeader(String name, String value)177 public void setHeader (String name, String value) { 178 headers.put(name, value); 179 } 180 181 /** Sets the content to be used in the HTTP request. 182 * @param content A string encoded in the corresponding Content-Encoding set in the headers, with the data to send with the 183 * HTTP request. For example, in case of HTTP GET, the content is used as the query string of the GET while on a 184 * HTTP POST it is used to send the POST data. */ setContent(String content)185 public void setContent (String content) { 186 this.content = content; 187 } 188 189 /** Sets the content as a stream to be used for a POST for example, to transmit custom data. 190 * @param contentStream The stream with the content data. */ setContent(InputStream contentStream, long contentLength)191 public void setContent (InputStream contentStream, long contentLength) { 192 this.contentStream = contentStream; 193 this.contentLength = contentLength; 194 } 195 196 /** Sets the time to wait for the HTTP request to be processed, use 0 block until it is done. The timeout is used for both 197 * the timeout when establishing TCP connection, and the timeout until the first byte of data is received. 198 * @param timeOut the number of milliseconds to wait before giving up, 0 or negative to block until the operation is done */ setTimeOut(int timeOut)199 public void setTimeOut (int timeOut) { 200 this.timeOut = timeOut; 201 } 202 203 /** Sets whether 301 and 302 redirects are followed. By default true. Can't be changed in the GWT backend because this uses 204 * XmlHttpRequests which always redirect. 205 * @param followRedirects whether to follow redirects. 206 * @exception IllegalArgumentException if redirection is disabled on the GWT backend. */ setFollowRedirects(boolean followRedirects)207 public void setFollowRedirects (boolean followRedirects) throws IllegalArgumentException { 208 if (followRedirects == true || Gdx.app.getType() != ApplicationType.WebGL) { 209 this.followRedirects = followRedirects; 210 } else { 211 throw new IllegalArgumentException("Following redirects can't be disabled using the GWT/WebGL backend!"); 212 } 213 } 214 215 /** Sets whether a cross-origin request will include credentials. Only used on GWT backend to allow cross-origin requests 216 * to include credentials such as cookies, authorization headers, etc... */ setIncludeCredentials(boolean includeCredentials)217 public void setIncludeCredentials (boolean includeCredentials) { 218 this.includeCredentials = includeCredentials; 219 } 220 221 /** Sets the HTTP method of the HttpRequest. */ setMethod(String httpMethod)222 public void setMethod (String httpMethod) { 223 this.httpMethod = httpMethod; 224 } 225 226 /** Returns the timeOut of the HTTP request. 227 * @return the timeOut. */ getTimeOut()228 public int getTimeOut () { 229 return timeOut; 230 } 231 232 /** Returns the HTTP method of the HttpRequest. */ getMethod()233 public String getMethod () { 234 return httpMethod; 235 } 236 237 /** Returns the URL of the HTTP request. */ getUrl()238 public String getUrl () { 239 return url; 240 } 241 242 /** Returns the content string to be used for the HTTP request. */ getContent()243 public String getContent () { 244 return content; 245 } 246 247 /** Returns the content stream. */ getContentStream()248 public InputStream getContentStream () { 249 return contentStream; 250 } 251 252 /** Returns the content length in case content is a stream. */ getContentLength()253 public long getContentLength () { 254 return contentLength; 255 } 256 257 /** Returns a Map<String, String> with the headers of the HTTP request. */ getHeaders()258 public Map<String, String> getHeaders () { 259 return headers; 260 } 261 262 /** Returns whether 301 and 302 redirects are followed. By default true. Whether to follow redirects. */ getFollowRedirects()263 public boolean getFollowRedirects () { 264 return followRedirects; 265 } 266 267 /** Returns whether a cross-origin request will include credentials. By default false. */ getIncludeCredentials()268 public boolean getIncludeCredentials () { 269 return includeCredentials; 270 } 271 272 @Override reset()273 public void reset () { 274 httpMethod = null; 275 url = null; 276 headers.clear(); 277 timeOut = 0; 278 279 content = null; 280 contentStream = null; 281 contentLength = 0; 282 283 followRedirects = true; 284 } 285 286 } 287 288 /** Listener to be able to do custom logic once the {@link HttpResponse} is ready to be processed, register it with 289 * {@link Net#sendHttpRequest(HttpRequest, HttpResponseListener)}. */ 290 public static interface HttpResponseListener { 291 292 /** Called when the {@link HttpRequest} has been processed and there is a {@link HttpResponse} ready. Passing data to the 293 * rendering thread should be done using {@link Application#postRunnable(java.lang.Runnable runnable)} {@link HttpResponse} 294 * contains the {@link HttpStatus} and should be used to determine if the request was successful or not (see more info at 295 * {@link HttpStatus#getStatusCode()}). For example: 296 * 297 * <pre> 298 * HttpResponseListener listener = new HttpResponseListener() { 299 * public void handleHttpResponse (HttpResponse httpResponse) { 300 * HttpStatus status = httpResponse.getStatus(); 301 * if (status.getStatusCode() >= 200 && status.getStatusCode() < 300) { 302 * // it was successful 303 * } else { 304 * // do something else 305 * } 306 * } 307 * } 308 * </pre> 309 * 310 * @param httpResponse The {@link HttpResponse} with the HTTP response values. */ handleHttpResponse(HttpResponse httpResponse)311 void handleHttpResponse (HttpResponse httpResponse); 312 313 /** Called if the {@link HttpRequest} failed because an exception when processing the HTTP request, could be a timeout any 314 * other reason (not an HTTP error). 315 * @param t If the HTTP request failed because an Exception, t encapsulates it to give more information. */ failed(Throwable t)316 void failed (Throwable t); 317 cancelled()318 void cancelled (); 319 } 320 321 /** Process the specified {@link HttpRequest} and reports the {@link HttpResponse} to the specified {@link HttpResponseListener} 322 * . 323 * @param httpRequest The {@link HttpRequest} to be performed. 324 * @param httpResponseListener The {@link HttpResponseListener} to call once the HTTP response is ready to be processed. Could 325 * be null, in that case no listener is called. */ sendHttpRequest(HttpRequest httpRequest, HttpResponseListener httpResponseListener)326 public void sendHttpRequest (HttpRequest httpRequest, HttpResponseListener httpResponseListener); 327 cancelHttpRequest(HttpRequest httpRequest)328 public void cancelHttpRequest (HttpRequest httpRequest); 329 330 /** Protocol used by {@link Net#newServerSocket(Protocol, int, ServerSocketHints)} and 331 * {@link Net#newClientSocket(Protocol, String, int, SocketHints)}. 332 * @author mzechner */ 333 public enum Protocol { 334 TCP 335 } 336 337 /** Creates a new server socket on the given address and port, using the given {@link Protocol}, waiting for incoming connections. 338 * 339 * @param hostname the hostname or ip address to bind the socket to 340 * @param port the port to listen on 341 * @param hints additional {@link ServerSocketHints} used to create the socket. Input null to use the default setting provided 342 * by the system. 343 * @return the {@link ServerSocket} 344 * @throws GdxRuntimeException in case the socket couldn't be opened */ newServerSocket(Protocol protocol, String hostname, int port, ServerSocketHints hints)345 public ServerSocket newServerSocket (Protocol protocol, String hostname, int port, ServerSocketHints hints); 346 347 /** Creates a new server socket on the given port, using the given {@link Protocol}, waiting for incoming connections. 348 * 349 * @param port the port to listen on 350 * @param hints additional {@link ServerSocketHints} used to create the socket. Input null to use the default setting provided 351 * by the system. 352 * @return the {@link ServerSocket} 353 * @throws GdxRuntimeException in case the socket couldn't be opened */ newServerSocket(Protocol protocol, int port, ServerSocketHints hints)354 public ServerSocket newServerSocket (Protocol protocol, int port, ServerSocketHints hints); 355 356 /** Creates a new TCP client socket that connects to the given host and port. 357 * 358 * @param host the host address 359 * @param port the port 360 * @param hints additional {@link SocketHints} used to create the socket. Input null to use the default setting provided by the 361 * system. 362 * @return GdxRuntimeException in case the socket couldn't be opened */ newClientSocket(Protocol protocol, String host, int port, SocketHints hints)363 public Socket newClientSocket (Protocol protocol, String host, int port, SocketHints hints); 364 365 /** Launches the default browser to display a URI. If the default browser is not able to handle the specified URI, the 366 * application registered for handling URIs of the specified type is invoked. The application is determined from the protocol 367 * and path of the URI. A best effort is made to open the given URI; however, since external applications are involved, no guarantee 368 * can be made as to whether the URI was actually opened. If it is known that the URI was not opened, false will be returned; 369 * otherwise, true will be returned. 370 * 371 * @param URI the URI to be opened. 372 * @return false if it is known the uri was not opened, true otherwise. */ openURI(String URI)373 public boolean openURI (String URI); 374 } 375