/** * $RCSfile$ * $Revision: $ * $Date: $ * * Copyright (C) 2006 Jive Software. All rights reserved. * * This software is published under the terms of the GNU Public License (GPL), * a copy of which is included in this distribution. */ package org.jivesoftware.multiplexer.net.http; import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlPullParserException; import org.jivesoftware.multiplexer.net.MXParser; import org.jivesoftware.util.Log; import org.dom4j.io.XMPPPacketReader; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.DocumentHelper; import org.mortbay.util.ajax.ContinuationSupport; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.ServletException; import java.io.IOException; /** * Handles requests to the HTTP Bind service. * * @author Alexander Wenckus */ public class HttpBindServlet extends HttpServlet { private HttpSessionManager sessionManager; private static XmlPullParserFactory factory; static { try { factory = XmlPullParserFactory.newInstance(MXParser.class.getName(), null); } catch (XmlPullParserException e) { Log.error("Error creating a parser factory", e); } } HttpBindServlet(HttpSessionManager sessionManager) { this.sessionManager = sessionManager; } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (isContinuation(request, response)) { return; } Document document; try { document = createDocument(request); } catch (Exception e) { Log.warn("Error parsing user request. [" + request.getRemoteAddr() + "]"); response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unable to parse request content: " + e.getMessage()); return; } Element node = document.getRootElement(); if (node == null || !"body".equals(node.getName())) { Log.warn("Body missing from request content. [" + request.getRemoteAddr() + "]"); response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Body missing from request content."); return; } String sid = node.attributeValue("sid"); // We have a new session if (sid == null) { createNewSession(request, response, node); } else { handleSessionRequest(sid, request, response, node); } } private boolean isContinuation(HttpServletRequest request, HttpServletResponse response) throws IOException { HttpConnection connection = (HttpConnection) request.getAttribute("request-connection"); if (connection == null) { return false; } synchronized (connection.getSession()) { respond(response, connection); } return true; } private void handleSessionRequest(String sid, HttpServletRequest request, HttpServletResponse response, Element rootNode) throws IOException { long rid = getLongAttribue(rootNode.attributeValue("rid"), -1); if (rid <= 0) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Body missing RID (Request ID)"); return; } HttpSession session = sessionManager.getSession(sid); if (session == null) { Log.warn("Client provided invalid session: " + sid + ". [" + request.getRemoteAddr() + "]"); response.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid SID."); return; } synchronized (session) { HttpConnection connection; try { connection = sessionManager.forwardRequest(rid, session, request.isSecure(), rootNode); } catch (HttpBindException e) { response.sendError(e.getHttpError(), e.getMessage()); if(e.shouldCloseSession()) { session.close(); } return; } catch (HttpConnectionClosedException nc) { Log.error("Error sending packet to client.", nc); return; } String type = rootNode.attributeValue("type"); if ("terminate".equals(type)) { session.close(); respond(response, createEmptyBody().getBytes("utf-8")); } else { connection .setContinuation(ContinuationSupport.getContinuation(request, connection)); request.setAttribute("request-connection", connection); respond(response, connection); } } } private void createNewSession(HttpServletRequest request, HttpServletResponse response, Element rootNode) throws IOException { long rid = getLongAttribue(rootNode.attributeValue("rid"), -1); if (rid <= 0) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Body missing RID (Request ID)"); return; } try { HttpConnection connection = new HttpConnection(rid, request.isSecure()); connection.setSession(sessionManager.createSession(rootNode, connection)); respond(response, connection); } catch (HttpBindException e) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } private void respond(HttpServletResponse response, HttpConnection connection) throws IOException { byte[] content; try { content = connection.getDeliverable().getBytes("utf-8"); } catch (HttpBindTimeoutException e) { content = createEmptyBody().getBytes("utf-8"); } respond(response, content); } private void respond(HttpServletResponse response, byte [] content) throws IOException { response.setStatus(HttpServletResponse.SC_OK); response.setContentType("text/xml"); response.setCharacterEncoding("utf-8"); response.setContentLength(content.length); response.getOutputStream().write(content); } private String createEmptyBody() { Element body = DocumentHelper.createElement("body"); body.addNamespace("", "http://jabber.org/protocol/httpbind"); return body.asXML(); } private long getLongAttribue(String value, long defaultValue) { if (value == null || "".equals(value)) { return defaultValue; } try { return Long.valueOf(value); } catch (Exception ex) { return defaultValue; } } private Document createDocument(HttpServletRequest request) throws DocumentException, IOException, XmlPullParserException { // Reader is associated with a new XMPPPacketReader XMPPPacketReader reader = new XMPPPacketReader(); reader.setXPPFactory(factory); return reader.read(request.getInputStream()); } }