/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jena.fuseki.conneg; import static org.apache.jena.fuseki.HttpNames.hAcceptCharset ; import javax.servlet.http.HttpServletRequest ; import org.apache.jena.atlas.web.AcceptList ; import org.apache.jena.atlas.web.MediaRange ; import org.apache.jena.atlas.web.MediaType ; import org.slf4j.Logger ; import org.slf4j.LoggerFactory ; /** * <p>Content negotiation is a mechanism defined in the HTTP specification * that makes it possible to serve different versions of a document * (or more generally, a resource representation) at the same URI, so that * user agents can specify which version fit their capabilities the best.</p> * * <p>ConNeg is used in Fuseki to help matching the content media type requested * by the user, against the list of offered media types.</p> * * @see <a href="http://en.wikipedia.org/wiki/Content_negotiation">http://en.wikipedia.org/wiki/Content_negotiation</a> * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1">http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1</a> */ public class ConNeg { private static Logger log = LoggerFactory.getLogger(ConNeg.class) ; // See riot.ContentNeg (client side). // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 /** * Parses the content type. It splits the string by semi-colon and finds the * other features such as the "q" quality factor. For a complete documentation * on how the parsing happens, see * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1">http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1</a>. * * @param contentType content type string * @return parsed media type */ static public MediaType parse(String contentType) { try { return MediaType.create(contentType) ; } catch (RuntimeException ex) { return null ; } } /** * <p>Creates a {@link AcceptList} with the given HTTP header string and * uses the {@link AcceptList#match(MediaType)} method to decide which * media type matches the HTTP header string.</p> * * <p>The <em>q</em> quality factor is used to decide which choice is the best * match.</p> * * @param headerString HTTP header string * @param offerList accept list * @return matched media type */ static public MediaType match(String headerString, AcceptList offerList) { AcceptList l = new AcceptList(headerString) ; return AcceptList.match(l, offerList) ; } /** * Match a single media type against a header string. * * @param headerString HTTP header string * @param mediaRangeStr Semi-colon separated list of media types * @return the matched media type or <code>null</code> if there was no match */ public static String match(String headerString, String mediaRangeStr) { AcceptList l = new AcceptList(headerString) ; MediaRange aItem = new MediaRange(mediaRangeStr) ; // MediaType MediaType m = l.match(aItem) ; if ( m == null ) return null ; return m.toHeaderString() ; } /** * Split and trims a string using a given regex. * * @param s string * @param splitStr given regex * @return an array with the trimmed strings found */ /*package*/ static String[] split(String s, String splitStr) { String[] x = s.split(splitStr,2) ; for ( int i = 0 ; i < x.length ; i++ ) { x[i] = x[i].trim() ; } return x ; } /** * <p>Chooses the charset by using the Accept-Charset HTTP header.</p> * * <p>See {@link ConNeg#choose(String, AcceptList, MediaType)}.</p> * * @param httpRequest HTTP request * @param myPrefs accept list * @param defaultMediaType default media type * @return media type chosen */ public static MediaType chooseCharset(HttpServletRequest httpRequest, AcceptList myPrefs, MediaType defaultMediaType) { String a = httpRequest.getHeader(hAcceptCharset) ; if ( log.isDebugEnabled() ) log.debug("Accept-Charset request: "+a) ; MediaType item = choose(a, myPrefs, defaultMediaType) ; if ( log.isDebugEnabled() ) log.debug("Charset chosen: "+item) ; return item ; } /** * <p>Choose the content media type by extracting the Accept HTTP header from * the HTTP request and choosing * (see {@link ConNeg#choose(String, AcceptList, MediaType)}) a content media * type that matches the header.</p> * * @param httpRequest HTTP request * @param myPrefs accept list * @param defaultMediaType default media type * @return media type chosen */ public static MediaType chooseContentType(HttpServletRequest httpRequest, AcceptList myPrefs, MediaType defaultMediaType) { String a = WebLib.getAccept(httpRequest) ; if ( log.isDebugEnabled() ) log.debug("Accept request: "+a) ; MediaType item = choose(a, myPrefs, defaultMediaType) ; if ( log.isDebugEnabled() ) log.debug("Content type chosen: "+item) ; return item ; } /** * <p>This method receives a HTTP header string, an {@link AcceptList} and a * default {@link MediaType}.</p> * * <p>If the header string is null, it returns the given default MediaType.</p> * * <p>Otherwise it builds an {@link AcceptList} object with the header string * and uses it to match against the given MediaType.</p> * * @param headerString HTTP header string * @param myPrefs accept list * @param defaultMediaType default media type * @return a media type or <code>null</code> if none matched or if the * HTTP header string and the default media type are <code>null</code>. */ private static MediaType choose(String headerString, AcceptList myPrefs, MediaType defaultMediaType) { if ( headerString == null ) return defaultMediaType ; AcceptList headerList = new AcceptList(headerString) ; if ( myPrefs == null ) { MediaType i = headerList.first() ; if ( i == null ) return defaultMediaType ; return i ; } MediaType i = AcceptList.match(headerList, myPrefs) ; if ( i == null ) return defaultMediaType ; return i ; } }