001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2009 Sun Microsystems Inc. 015 * Portions Copyright 2010–2011 ApexIdentity Inc. 016 * Portions Copyright 2011-2014 ForgeRock AS. 017 */ 018 019package org.forgerock.openig.http; 020 021import static org.forgerock.openig.el.Functions.*; 022 023import java.io.IOException; 024import java.net.URISyntaxException; 025import java.util.LinkedHashMap; 026import java.util.List; 027 028import org.forgerock.openig.util.MultiValueMap; 029 030/** 031 * Form fields, a case-sensitive multi-string-valued map. The form can be read 032 * from and written to request objects as query parameters (GET) and request 033 * entities (POST). 034 */ 035public class Form extends MultiValueMap<String, String> { 036 037 /** 038 * Constructs a new, empty form object. 039 */ 040 public Form() { 041 super(new LinkedHashMap<String, List<String>>()); 042 } 043 044 /** 045 * Parses a URL-encoded string containing form parameters and stores them in 046 * this object. Malformed name-value pairs (missing the "=" delimiter) are 047 * simply ignored. 048 * 049 * @param s the URL-encoded string to parse. 050 * @return this form object. 051 */ 052 public Form fromString(String s) { 053 for (String param : s.split("&")) { 054 String[] nv = param.split("=", 2); 055 if (nv.length == 2) { 056 add(urlDecode(nv[0]), urlDecode(nv[1])); 057 } 058 } 059 return this; 060 } 061 062 /** 063 * Returns this form in a URL-encoded format string. 064 * 065 * @return the URL-encoded form. 066 */ 067 @Override 068 public String toString() { 069 StringBuilder sb = new StringBuilder(); 070 for (String name : keySet()) { 071 for (String value : get(name)) { 072 if (sb.length() > 0) { 073 sb.append('&'); 074 } 075 sb.append(urlEncode(name)).append('=').append(urlEncode(value)); 076 } 077 } 078 return sb.toString(); 079 } 080 081 /** 082 * Parses the query parameters of a request and stores them in this object. 083 * The object is not cleared beforehand, so this adds to any fields already 084 * in place. 085 * 086 * @param request the request to be parsed. 087 * @return this form object. 088 */ 089 public Form fromRequestQuery(Request request) { 090 String query = request.getUri().getRawQuery(); 091 if (query != null) { 092 fromString(query); 093 } 094 return this; 095 } 096 097 /** 098 * Sets a request URI with query parameters. This overwrites any query 099 * parameters that may already exist in the request URI. 100 * 101 * @param request the request to set query parameters to. 102 */ 103 public void toRequestQuery(Request request) { 104 try { 105 request.getUri().setRawQuery(toString()); 106 } catch (URISyntaxException use) { 107 throw new IllegalArgumentException(use); 108 } 109 } 110 111 /** 112 * Appends the form as additional query parameters on an existing request 113 * URI. This leaves any existing query parameters intact. 114 * 115 * @param request the request to append query parameters to. 116 */ 117 public void appendRequestQuery(Request request) { 118 StringBuilder sb = new StringBuilder(); 119 String uriQuery = request.getUri().getRawQuery(); 120 if (uriQuery != null && uriQuery.length() > 0) { 121 sb.append(uriQuery); 122 } 123 String toAppend = toString(); 124 if (toAppend != null && toAppend.length() > 0) { 125 if (sb.length() > 0) { 126 sb.append('&'); 127 } 128 sb.append(toAppend); 129 } 130 String newQuery = sb.toString(); 131 if (newQuery.length() == 0) { 132 newQuery = null; 133 } 134 try { 135 request.getUri().setRawQuery(newQuery); 136 } catch (URISyntaxException use) { 137 throw new IllegalArgumentException(use); 138 } 139 } 140 141 /** 142 * Parses the URL-encoded form entity of a request and stores them in this 143 * object. The object is not cleared beforehand, so this adds to any fields 144 * already in place. 145 * 146 * @param request 147 * the request to be parsed. 148 * @return this form object. 149 * @throws IOException 150 * if an I/O exception occurs. 151 */ 152 public Form fromRequestEntity(Request request) throws IOException { 153 if (request != null 154 && request.getEntity() != null 155 && "application/x-www-form-urlencoded".equalsIgnoreCase(request.getHeaders() 156 .getFirst("Content-Type"))) { 157 fromString(request.getEntity().getString()); 158 } 159 return this; 160 } 161 162 /** 163 * Populates a request with the necessary headers and entity for the form to 164 * be submitted as a POST with application/x-www-form-urlencoded content 165 * type. This overwrites any entity that may already be in the request. 166 * 167 * @param request the request to add the form entity to. 168 */ 169 public void toRequestEntity(Request request) { 170 String form = toString(); 171 request.setMethod("POST"); 172 request.getHeaders().putSingle("Content-Type", "application/x-www-form-urlencoded"); 173 request.getHeaders().putSingle("Content-Length", Integer.toString(form.length())); 174 request.getEntity().setString(form); 175 } 176}