/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.plugins.platform.content.yum; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Represents a yum/http request made by a yum client. * * @author jortel */ class Request { /** * The yum server. */ final YumServer server; /** * An open client (accepted socket) */ private final Socket socket; /** * A yum repomd metadata object. */ private final Repomd repomd; /** * A current yum primary metadata object. */ private final Primary primary; /** * The http <i>filename</i> contained in the URL for the request. */ String filename; /** * The http <i>args</i> contained in the URL for the request. */ Map<String, String> args; /** * The http <i>header</i> fields. */ Map<String, String> fields; private final Log log = LogFactory.getLog(Request.class); /** * Constructed with the server and client socket. * * @param server The yum server that accepted the request. * @param socket An open/conditioned client socket. */ Request(YumServer server, Socket socket) { this.server = server; this.socket = socket; repomd = new Repomd(this); primary = new Primary(this); } /** * Returns the current yum context. * * @return A yum context. */ YumContext context() { return server.context; } /** * Process the yum (http) request. This class provides bare-bones http request processing. * * <p/>* Read and process the header into a map of header fields and args.. * * <p/>* Reply to the request. */ void process() { try { BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String request = reader.readLine(); if (request!=null) { StringTokenizer st = new StringTokenizer(request); st.nextToken(); // skip request-type String url = st.nextToken(); log.info("processing: " + url); String path = url.substring(context().basepath().length() + 1); int qmark = qmark(path); filename = path.substring(0, qmark); args = getArgs(path.substring(qmark)); fields = httpFields(reader); reply(); } } catch (Exception e) { log.error("request failed:", e); } finally { close(socket); } } /** * Clean the metadata. Delete locally cached metadata files. */ void cleanMetadata() { repomd.delete(); primary.delete(); } /** * Return the index of the (?) character in the URL. * * @param path A URL path string. * * @return The index when found, else the string length. */ private int qmark(String path) { int qmark = path.indexOf('?'); return ((qmark == -1) ? path.length() : qmark); } /** * Build and return a map containing the <i>args</i> part of the URL string. * * @param path The path segment of the URL string. * * @return A map containing the URL args. */ private Map<String, String> getArgs(String path) { if (path.length() < 4) { return Collections.emptyMap(); } path = path.substring(1); Map<String, String> result = new HashMap<String, String>(); for (String arg : path.split(",")) { int eq = arg.indexOf("="); String name = arg.substring(0, eq); String value = arg.substring(eq + 1); result.put(name.trim(), value.trim()); } return result; } /** * Reply to the request using the appropriate content object. * * @throws Exception On all errors. */ private void reply() throws Exception { Content content = selectContent(filename); if (content == null) { content = new Package(this); } OutputStream ostr = socket.getOutputStream(); content.writeHeader(ostr); content.writeContent(ostr); ostr.close(); } /** * Select the appropriate content object based on the file specified in the request URL. Selects a metadata object * when identified by name, else it is assumed that the cotent is a package request. * * @param filename The request protion of the URL string. * * @return The content object specified by name in the request. */ private Content selectContent(String filename) { if (filename.equals("repodata/repomd.xml")) { repomd.delete(); if (primary.stale()) { primary.delete(); } return repomd; } if (filename.equals("repodata/primary.xml")) { return primary; } return null; } /** * Process the http request using the specified reader and build a map containing the names and values of all of the * http header fields. * * @param reader A reader opened on the http request input stream. * * @return A map containing the http header fields. * * @throws Exception On all errors. */ private Map<String, String> httpFields(BufferedReader reader) throws Exception { Map<String, String> result = new HashMap<String, String>(); while (true) { String line = reader.readLine(); if (line!= null && line.length() > 0) { String[] pair = line.split(":"); result.put(pair[0], pair[1].trim()); } else { break; } } return result; } /** * Quietly close the specified socket. Mitigates the <u>really</u> annoying pattern by <i>java.net/java.io</i> * objects that throw an exception on close() operations which <u>really</u> sucks when trying to close resources in * the finally clause. * * @param socket An open socket. */ void close(Socket socket) { try { socket.close(); } catch (Exception e) { log.error("close", e); } } }