org.expath.httpclient.impl.RequestParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of http-client-java Show documentation
Show all versions of http-client-java Show documentation
Java Library implementing the core HTTP Client module features
The newest version!
/****************************************************************************/
/* File: RequestParser.java */
/* Author: F. Georges - H2O Consulting */
/* Date: 2011-03-10 */
/* Tags: */
/* Copyright (c) 2011 Florent Georges (see end of file.) */
/* ------------------------------------------------------------------------ */
package org.expath.httpclient.impl;
import org.expath.httpclient.*;
import org.expath.tools.ToolsException;
import org.expath.tools.model.Attribute;
import org.expath.tools.model.Element;
import org.expath.tools.model.Sequence;
/**
* Parse the http:request element into a {@link HttpRequest} object.
*
* @author Florent Georges
*/
public class RequestParser
{
public RequestParser(Element request)
throws HttpClientException
{
String ns = request.getNamespaceUri();
String name = request.getLocalName();
boolean ns_ok = true;
if ( HttpConstants.HTTP_NS_URI.equals(ns) ) {
myNs = HttpConstants.HTTP_NS_URI;
myOtherNs = HttpConstants.HTTP_CLIENT_NS_URI;
}
else if ( HttpConstants.HTTP_CLIENT_NS_URI.equals(ns) ) {
myNs = HttpConstants.HTTP_CLIENT_NS_URI;
myOtherNs = HttpConstants.HTTP_NS_URI;
}
else {
ns_ok = false;
}
if ( ! "request".equals(name) || ! ns_ok ) {
String clark = "{" + ns + "}" + name;
throw new HttpClientException(HttpClientError.HC005, "$request is not an element(http:request), but is " + clark);
}
myRequest = request;
myNs = ns;
}
public String getNamespaceURI()
{
return myNs;
}
public HttpCredentials getCredentials()
{
return myCredentials;
}
public HttpRequest parse(Sequence bodies, String href)
throws HttpClientException
{
String username = null;
String password = null;
String auth_method = null;
HttpRequest req = new HttpRequestImpl();
req.setHref(href);
// walk the attributes:
// method = NCName
// href? = anyURI
// http-version = string
// status-only? = boolean
// username? = string
// password? = string
// auth-method? = string
// send-authorization? = boolean
// override-media-type? = string
// follow-redirect? = boolean
// timeout? = integer
// gzip? = boolean
// chunked? = boolean
for ( Attribute a : myRequest.attributes() ) {
String local = a.getLocalName();
if ( !(a.getNamespaceUri() == null || a.getNamespaceUri().isEmpty()) ) {
// ignore namespace qualified attributes
}
else if ( "method".equals(local) ) {
req.setMethod(a.getValue());
}
else if ( "href".equals(local) ) {
req.setHref(a.getValue());
}
else if ( "http-version".equals(local) ) {
req.setHttpVersion(a.getValue().trim());
}
else if ( "status-only".equals(local) ) {
req.setStatusOnly(toBoolean(a));
}
else if ( "username".equals(local) ) {
username = a.getValue();
}
else if ( "password".equals(local) ) {
password = a.getValue();
}
else if ( "auth-method".equals(local) ) {
auth_method = a.getValue();
}
else if ( "send-authorization".equals(local) ) {
req.setPreemptiveAuthentication(toBoolean(a));
}
else if ( "default-charset".equals(local) ) {
req.setDefaultCharset(a.getValue());
}
else if ( "override-media-type".equals(local) ) {
req.setOverrideType(a.getValue());
}
else if ( "follow-redirect".equals(local) ) {
req.setFollowRedirect(toBoolean(a));
}
else if ( "timeout".equals(local) ) {
req.setTimeout(toInteger(a));
}
else if ( "gzip".equals(local) ) {
req.setGzip(toBoolean(a));
}
else if ( "chunked".equals(local) ) {
req.setChunked(toBoolean(a));
}
else {
throw new HttpClientException(HttpClientError.HC005, "Unknown attribute http:request/@" + local);
}
}
if ( req.getMethod() == null ) {
throw new HttpClientException(HttpClientError.HC005, "required @method has not been set on http:request");
}
if ( req.getHref() == null ) {
throw new HttpClientException(HttpClientError.HC005, "required @href has not been set on http:request");
}
if ( username != null || password != null || auth_method != null ) {
setAuthentication(username, password, auth_method);
}
if(req.getHttpVersion() != null && req.getHttpVersion().equals(HttpConstants.HTTP_1_0) && req.isChunked()) {
throw new HttpClientException(HttpClientError.HC005, "Chunked transfer encoding can only be used with HTTP 1.1");
}
// walk the elements
// TODO: Check element structure validity (header*, (multipart|body)?)
HeaderSet headers = new HeaderSet();
req.setHeaders(headers);
for ( Element child : myRequest.children() ) {
String local = child.getLocalName();
String ns = child.getNamespaceUri();
if ( ns == null || ns.isEmpty() ) {
// elements in no namespace are an error
throw new HttpClientException(HttpClientError.HC005, "Element in no namespace: " + local);
}
else if ( myOtherNs.equals(ns) ) {
String clark = "{" + ns + "}" + local;
throw new HttpClientException(HttpClientError.HC005, "http:request mixes elements in the new and legacy HTTP namespace: " + clark);
}
else if ( ! myNs.equals(ns) ) {
// ignore elements in other namespaces
}
else if ( "header".equals(local) ) {
addHeader(headers, child);
}
else if ( "body".equals(local) || "multipart".equals(local) ) {
HttpRequestBody b = BodyFactory.makeRequestBody(child, bodies, myNs);
req.setBody(b);
}
else {
throw new HttpClientException(HttpClientError.HC005, "Unknown element: " + local);
}
}
return req;
}
private void setAuthentication(String user, String pwd, String method)
throws HttpClientException
{
if ( user == null || pwd == null || method == null ) {
throw new HttpClientException(HttpClientError.HC005, "@username, @password and @auth-method must be all set");
}
if ( "basic".equals(method) ) {
myCredentials = new HttpCredentials(user, pwd, method);
}
else if ( "digest".equals(method) ) {
// FIXME: Wrong if HREF is not on http:request, but as a param, because
// it will be set on myRequest after this method has been called.
myCredentials = new HttpCredentials(user, pwd, method);
}
else {
throw new HttpClientException(HttpClientError.HC005, "Unknown authentication method: " + method);
}
}
private void addHeader(HeaderSet headers, Element e)
throws HttpClientException
{
String name = null;
String value = null;
for ( Attribute a : e.attributes() ) {
String local = a.getLocalName();
if ( !(a.getNamespaceUri() == null || a.getNamespaceUri().isEmpty()) ) {
// ignore namespace qualified attributes
}
else if ( "name".equals(local) ) {
name = a.getValue();
}
else if ( "value".equals(local) ) {
value = a.getValue();
}
else {
throw new HttpClientException(HttpClientError.HC005, "Unknown attribute http:header/@" + local);
}
}
// both are required
if ( name == null || value == null ) {
throw new HttpClientException(HttpClientError.HC005, "@name and @value are required on http:header");
}
if(name.equalsIgnoreCase("Content-Length")) {
throw new HttpClientException(HttpClientError.HC005, "Content-Length should not be explicitly provided, either it will automatically be added or Transfer-Encoding will be used.");
}
if(name.equalsIgnoreCase("Transfer-Encoding")) {
throw new HttpClientException(HttpClientError.HC005, "Transfer-Encoding should not be explicitly provided, it will automatically be added if required.");
}
// actually add the header
headers.add(name, value);
}
/**
* Helper function to handle the exception.
*
* @return The attribute value as a boolean.
*
* @throws HttpClientException If the attribute value is not a proper boolean.
*/
private boolean toBoolean(Attribute a)
throws HttpClientException
{
try {
return a.getBoolean();
}
catch ( ToolsException ex ) {
throw new HttpClientException(HttpClientError.HC005, "Error parsing the attribute as a boolean", ex);
}
}
/**
* Helper function to handle the exception.
*
* @return The attribute value as a integer.
*
* @throws HttpClientException If the attribute value is not a proper integer.
*/
private int toInteger(Attribute a)
throws HttpClientException
{
try {
return a.getInteger();
}
catch ( ToolsException ex ) {
throw new HttpClientException(HttpClientError.HC005, "Error parsing the attribute as an integer", ex);
}
}
/** The http:request element. */
private Element myRequest;
/** The namespace URI of the http:request element (either the new or the legacy URI). */
private String myNs;
/** The legacy namespace URI if myNs is the new one, or the other way around. */
private String myOtherNs;
/** User credentials in case of authentication (from @username, @password and @auth-method). */
private HttpCredentials myCredentials = null;
}
/* ------------------------------------------------------------------------ */
/* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS COMMENT. */
/* */
/* The contents of this file are subject to the Mozilla Public License */
/* Version 1.0 (the "License"); you may not use this file except in */
/* compliance with the License. You may obtain a copy of the License at */
/* http://www.mozilla.org/MPL/. */
/* */
/* Software distributed under the License is distributed on an "AS IS" */
/* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See */
/* the License for the specific language governing rights and limitations */
/* under the License. */
/* */
/* The Original Code is: all this file. */
/* */
/* The Initial Developer of the Original Code is Florent Georges. */
/* */
/* Contributor(s): none. */
/* ------------------------------------------------------------------------ */