001/* 002 * Stallion Core: A Modern Web Framework 003 * 004 * Copyright (C) 2015 - 2016 Stallion Software LLC. 005 * 006 * This program is free software: you can redistribute it and/or modify it under the terms of the 007 * GNU General Public License as published by the Free Software Foundation, either version 2 of 008 * the License, or (at your option) any later version. This program is distributed in the hope that 009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 011 * License for more details. You should have received a copy of the GNU General Public License 012 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. 013 * 014 * 015 * 016 */ 017 018package io.stallion.requests; 019 020import javax.persistence.Column; 021import javax.servlet.MultipartConfigElement; 022import javax.servlet.ServletException; 023import javax.servlet.http.Cookie; 024import javax.servlet.http.HttpServletRequest; 025import javax.servlet.http.Part; 026 027import com.fasterxml.jackson.core.type.TypeReference; 028import com.fasterxml.jackson.databind.ObjectMapper; 029import io.stallion.Context; 030import io.stallion.exceptions.ClientException; 031import io.stallion.plugins.javascript.Sandbox; 032import io.stallion.services.Log; 033import io.stallion.settings.Settings; 034import io.stallion.users.EmptyOrg; 035import io.stallion.users.EmptyUser; 036import io.stallion.users.IOrg; 037import io.stallion.users.IUser; 038import org.apache.commons.io.IOUtils; 039 040import org.apache.commons.lang3.StringUtils; 041import org.apache.http.NameValuePair; 042import org.apache.http.client.utils.URLEncodedUtils; 043import org.eclipse.jetty.server.Request; 044 045import java.io.BufferedReader; 046import java.io.IOException; 047import java.io.UnsupportedEncodingException; 048import java.net.URLDecoder; 049import java.nio.charset.Charset; 050import java.util.*; 051 052import static io.stallion.utils.Literals.*; 053 054public class StRequest implements IRequest { 055 private static final MultipartConfigElement MULTI_PART_CONFIG = new MultipartConfigElement(System.getProperty("java.io.tmpdir")); 056 057 058 private HttpServletRequest request; 059 private Request baseRequest; 060 private String path; 061 private String query = null; 062 private IUser user = new EmptyUser(); 063 private Long valetUserId = 0L; 064 private String valetEmail = ""; 065 private boolean scoped = false; 066 private Set<String> scopes; 067 private IOrg org = new EmptyOrg(); 068 private String rawRequestBody = null; 069 private Map<String, Object> bodyMap; 070 private String content = null; 071 private Object bodyObject = null; 072 private Boolean isJsonRequest = null; 073 private Map<String, String> queryParams = null; 074 private Map<String, Object> items = map(); 075 private SandboxedRequest sandboxedRequest; 076 private String scheme; 077 078 079 public StRequest() { 080 081 } 082 083 public StRequest(String path, Request baseRequest, HttpServletRequest request) { 084 this.setPath(path); 085 this.request = request; 086 this.baseRequest = baseRequest; 087 this.query = request.getQueryString(); 088 } 089 090 091 @Override 092 public void setAsMultiPartRequest() { 093 if (request.getContentType() != null && request.getContentType().startsWith("multipart/form-data")) { 094 baseRequest.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, MULTI_PART_CONFIG); 095 } 096 } 097 098 public HttpServletRequest getHttpServletRequest() { 099 return this.request; 100 } 101 102 103 104 public Part getPart(String name) { 105 try { 106 return request.getPart(name); 107 } catch (IOException e) { 108 throw new RuntimeException(e); 109 } catch (ServletException e) { 110 throw new RuntimeException(e); 111 } 112 } 113 114 /** 115 * Gets the absolute url of the original request. 116 * Reconstructs the URL using the x-forwarded-host, if necessary, 117 * so this will work in the case of proxies. 118 * @return 119 */ 120 @Override 121 public String requestUrl() { 122 String host = getHost(); 123 String proto = getScheme(); 124 String baseUrl = proto + "://" + host; 125 return baseUrl + this.getPath(); 126 } 127 128 129 130 @Override 131 public String getScheme() { 132 if (empty(scheme) && this.request != null) { 133 if (!empty(getHeader("x-upstream-forwarded-proto"))) { 134 scheme = getHeader("x-upstream-forwarded-proto"); 135 } 136 if (empty(scheme) && !empty(getHeader("x-forwarded-proto"))) { 137 scheme = getHeader("x-forwarded-proto"); 138 } 139 } 140 if (empty(scheme)) { 141 String baseUrl = ""; 142 if (Context.getSettings() != null ) { 143 baseUrl = Context.getSettings().getSiteUrl(); 144 } 145 if (baseUrl != null) { 146 int i = baseUrl.indexOf("://"); 147 if (i > -1) { 148 scheme = baseUrl.substring(0, i); 149 } 150 } 151 } 152 if (empty(scheme) && this.request != null) { 153 scheme = this.request.getScheme(); 154 } 155 if (empty(scheme)){ 156 scheme = "http"; 157 } 158 return scheme; 159 } 160 161 @Override 162 public String getHost() { 163 if (this.request == null) { 164 return "http://localhost" + this.getPath(); 165 } 166 String host = this.request.getHeader("host"); 167 if (!empty(getHeader("x-forwarded-host"))) { 168 host = getHeader("x-forwarded-host"); 169 } 170 return host; 171 } 172 173 @Override 174 public String getQueryString() { 175 if (query == null) { 176 if (!requestUrl().contains("?")) { 177 query = ""; 178 } else { 179 query = requestUrl().split("\\?", 2)[1]; 180 } 181 } 182 return query; 183 } 184 185 @Override 186 public String getRemoteAddr() { 187 if (request != null) { 188 return request.getRemoteAddr(); 189 } else { 190 return "0.0.0.0"; 191 } 192 } 193 194 @Override 195 public String getActualIp() { 196 if (!empty(Settings.instance().getIpHeaderName())) { 197 return or(request.getHeader(Settings.instance().getIpHeaderName()), getRemoteAddr()); 198 } 199 return getRemoteAddr(); 200 } 201 202 @Override 203 public Object getBodyObject(Class clazz) { 204 205 if (bodyObject == null) { 206 ObjectMapper mapper = new ObjectMapper(); 207 try { 208 bodyObject = mapper.readValue(getContent(), clazz); 209 } catch (IOException e) { 210 Log.exception(e, "Could not parse JSON body for the request: " + content); 211 throw new ClientException("Could not parse JSON body for the request."); 212 } 213 } 214 return bodyObject; 215 } 216 217 @Override 218 public Map<String, Object> getBodyMap() { 219 if (bodyMap == null) { 220 if (or(getHeader("Content-type"), "").startsWith("application/x-www-form-urlencoded")) { 221 bodyMap = new HashMap<>(); 222 223 for (NameValuePair pair: URLEncodedUtils.parse(getContent(), Charset.forName(or(request.getCharacterEncoding(), "UTF8")))) { 224 if (bodyMap.containsKey(pair.getName())) { 225 Object existing = bodyMap.get(pair.getName()); 226 if (existing instanceof List) { 227 ((List)existing).add(pair.getValue()); 228 } else { 229 bodyMap.put(pair.getName(), list(existing, pair.getValue())); 230 } 231 } else { 232 bodyMap.put(pair.getName(), pair.getValue()); 233 } 234 } 235 } else { 236 ObjectMapper mapper = new ObjectMapper(); 237 String content = getContent(); 238 if (StringUtils.isEmpty(content)) { 239 bodyMap = new HashMap<>(); 240 } else { 241 try { 242 bodyMap = mapper.readValue(getContent(), 243 new TypeReference<HashMap<String, Object>>() { 244 }); 245 } catch (IOException e) { 246 Log.exception(e, "Could not parse JSON body for the request: " + content); 247 throw new ClientException("Could not parse JSON body for the request."); 248 } 249 250 } 251 } 252 } 253 return bodyMap; 254 } 255 256 @Override 257 public Object getBodyParam(String name) { 258 259 return getBodyMap().getOrDefault(name, null); 260 } 261 262 @Override 263 public Map<String, String> getQueryParams() { 264 if (queryParams == null) { 265 queryParams = queryToParams(); 266 } 267 return queryParams; 268 } 269 270 271 private Map<String, String> queryToParams() { 272 Map<String, String> query_pairs = new HashMap<String, String>(); 273 if (empty(getQueryString())) { 274 return map(); 275 } 276 String[] pairs = getQueryString().split("&"); 277 for (String pair : pairs) { 278 int idx = pair.indexOf("="); 279 if (idx < 0) { 280 continue; 281 } 282 try { 283 query_pairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8")); 284 } catch (UnsupportedEncodingException e) { 285 Log.exception(e, "Error parsing url {0}", getQueryString()); 286 } 287 } 288 return query_pairs; 289 } 290 291 292 @Override 293 public Cookie[] getCookies() { 294 return this.request.getCookies(); 295 } 296 297 @Override 298 public Cookie getCookie(String cookieName) { 299 if (request.getCookies() != null) { 300 for (Cookie cookie : request.getCookies()) { 301 if (cookieName.equals(cookie.getName())) { 302 return cookie; 303 } 304 } 305 } 306 return null; 307 } 308 309 @Override 310 public String getPath() { 311 return path; 312 } 313 314 @Override 315 public void setPath(String path) { 316 this.path = path; 317 } 318 319 @Override 320 public String getHeader(String name) { 321 return this.request.getHeader(name); 322 } 323 324 @Override 325 public BufferedReader getReader() throws IOException { 326 return this.request.getReader(); 327 } 328 329 @Override 330 public String getContent() { 331 if (content == null) { 332 try { 333 //this.content = IOUtils.toString(getReader()); 334 this.content = IOUtils.toString(this.request.getInputStream(), "UTF8"); 335 //} catch (java.lang.IllegalStateException e) { 336 // try { 337 // this.content = IOUtils.toString(this.request.getInputStream()); 338 // } catch (IOException e2) { 339 // throw new RuntimeException(e); 340 // } 341 } catch (IOException e) { 342 throw new RuntimeException(e); 343 } 344 } 345 return content; 346 347 } 348 349 @Override 350 public Enumeration<String> getHeaderNames() { 351 if (request == null) { 352 //return (Enumeration<String>)new ArrayList<String>(); 353 return new Vector().elements(); 354 } else { 355 return request.getHeaderNames(); 356 } 357 358 } 359 360 361 362 363 364 @Override 365 public IUser getUser() { 366 return user; 367 } 368 369 @Override 370 public void setUser(IUser user) { 371 this.user = user; 372 } 373 374 @Override 375 public IOrg getOrg() { 376 return org; 377 } 378 379 @Override 380 public void setOrg(IOrg org) { 381 this.org = org; 382 } 383 384 @Override 385 public String getMethod() { 386 return this.request.getMethod(); 387 } 388 389 @Override 390 public String getParameter(String paramName) { 391 return this.request.getParameter(paramName); 392 } 393 394 @Override 395 public Boolean getIsJsonRequest() { 396 return isJsonRequest; 397 } 398 399 @Override 400 public void setIsJsonRequest(Boolean isJsonRequest) { 401 this.isJsonRequest = isJsonRequest; 402 } 403 404 @Override 405 public void setQuery(String query) { 406 this.query = query; 407 } 408 409 @Override 410 public Map<String, Object> getItems() { 411 return items; 412 } 413 414 @Override 415 public void setItems(Map<String, Object> items) { 416 this.items = items; 417 } 418 419 @Override 420 public SandboxedRequest getSandboxedRequest(Sandbox box) { 421 if (sandboxedRequest == null) { 422 sandboxedRequest = new SandboxedRequest(box, this); 423 } 424 return sandboxedRequest; 425 } 426 427 @Override 428 public Set<String> getScopes() { 429 return scopes; 430 } 431 432 @Override 433 public StRequest setScopes(Set<String> scopes) { 434 this.scopes = scopes; 435 return this; 436 } 437 438 @Override 439 public boolean isScoped() { 440 return scoped; 441 } 442 443 @Override 444 public StRequest setScoped(boolean scoped) { 445 this.scoped = scoped; 446 return this; 447 } 448 449 @Column 450 public Long getValetUserId() { 451 return valetUserId; 452 } 453 454 public StRequest setValetUserId(Long valetUserId) { 455 this.valetUserId = valetUserId; 456 return this; 457 } 458 459 @Column 460 public String getValetEmail() { 461 return valetEmail; 462 } 463 464 public StRequest setValetEmail(String valetEmail) { 465 this.valetEmail = valetEmail; 466 return this; 467 } 468}