`
zpball
  • 浏览: 930113 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Hessian源码分析和Hack --让Hessian携带远程调用端的信息

阅读更多
项目选定Hessian作为web service的实现方式,确实很轻量级,速度就跟直接用socket差不多,全是二进制传送节约了不少开销。但是在使用过程中有业务需要是必须获得远程端的ip地址,主机名等信息的。翻便Hessian的文档和google了n次未果,迫不得已到caucho和spring论坛去问,都没有得到答复。今天心一横把hessian的源代码加入到项目中单步跟踪,总算有点小收获。献丑分享出来,一方面给需要的朋友,主要还是希望各位找找是否存在bug,以及是否有更好的改良。

一:先撇开Spring不谈,来看看纯Hessian的调用
按照hessian文档里边介绍的demo,在web.xml里边如下配置

<servlet>   
 <servlet-name>hello</servlet-name>   
 <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>   
  <init-param>   
    <param-name>home-class</param-name>   
    <param-value>example.BasicService</param-value>   
  </init-param>   
  <init-param>   
    <param-name>home-api</param-name>   
    <param-value>example.Basic</param-value>   
  </init-param>   
</servlet>   
  
<servlet-mapping>   
  <url-pattern>/hello</url-pattern>   
  <servlet-name>hello</servlet-name>   
</servlet-mapping>  

   <servlet>
   <servlet-name>hello</servlet-name>
   <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
    <init-param>
      <param-name>home-class</param-name>
      <param-value>example.BasicService</param-value>
    </init-param>
    <init-param>
      <param-name>home-api</param-name>
      <param-value>example.Basic</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <url-pattern>/hello</url-pattern>
    <servlet-name>hello</servlet-name>
  </servlet-mapping>



由此可知Hessian调用的入口是HessianServlet这个Servlet,进去看看

 
/**  
 * Servlet for serving Hessian services.  
 */  
public class HessianServlet extends GenericServlet {   
  private Class _homeAPI;   
  private Object _homeImpl;   
     
  private Class _objectAPI;   
  private Object _objectImpl;   
     
  private HessianSkeleton _homeSkeleton;   
  private HessianSkeleton _objectSkeleton;   
  
  private SerializerFactory _serializerFactory;   
  
  public String getServletInfo()   
  {   
    return "Hessian Servlet";   
  }   
  
  /**  
   * Sets the home api.  
   */  
  public void setHomeAPI(Class api)   
  {   
    _homeAPI = api;   
  }   
  
  /**  
   * Sets the home implementation  
   */  
  public void setHome(Object home)   
  {   
    _homeImpl = home;   
  }   
  
  /**  
   * Sets the object api.  
   */  
  public void setObjectAPI(Class api)   
  {   
    _objectAPI = api;   
  }   
  
  /**  
   * Sets the object implementation  
   */  
  public void setObject(Object object)   
  {   
    _objectImpl = object;   
  }   
  
  /**  
   * Sets the service class.  
   */  
  public void setService(Object service)   
  {   
    setHome(service);   
  }   
  
  /**  
   * Sets the api-class.  
   */  
  public void setAPIClass(Class api)   
  {   
    setHomeAPI(api);   
  }   
  
  /**  
   * Gets the api-class.  
   */  
  public Class getAPIClass()   
  {   
    return _homeAPI;   
  }   
  
  /**  
   * Sets the serializer factory.  
   */  
  public void setSerializerFactory(SerializerFactory factory)   
  {   
    _serializerFactory = factory;   
  }   
  
  /**  
   * Gets the serializer factory.  
   */  
  public SerializerFactory getSerializerFactory()   
  {   
    if (_serializerFactory == null)   
      _serializerFactory = new SerializerFactory();   
  
    return _serializerFactory;   
  }   
  
  /**  
   * Sets the serializer send collection java type.  
   */  
  public void setSendCollectionType(boolean sendType)   
  {   
    getSerializerFactory().setSendCollectionType(sendType);   
  }   
  
  /**  
   * Initialize the service, including the service object.  
   */  
  public void init(ServletConfig config)   
    throws ServletException   
  {   
    super.init(config);   
       
    try {   
      if (_homeImpl != null) {   
      }   
      else if (getInitParameter("home-class") != null) {   
    String className = getInitParameter("home-class");   
       
    Class homeClass = loadClass(className);   
  
    _homeImpl = homeClass.newInstance();   
  
    init(_homeImpl);   
      }   
      else if (getInitParameter("service-class") != null) {   
    String className = getInitParameter("service-class");   
       
    Class homeClass = loadClass(className);   
  
    _homeImpl = homeClass.newInstance();   
       
    init(_homeImpl);   
      }   
      else {   
    if (getClass().equals(HessianServlet.class))   
      throw new ServletException("server must extend HessianServlet");   
  
    _homeImpl = this;   
      }   
  
      if (_homeAPI != null) {   
      }   
      else if (getInitParameter("home-api") != null) {   
    String className = getInitParameter("home-api");   
       
    _homeAPI = loadClass(className);   
      }   
      else if (getInitParameter("api-class") != null) {   
    String className = getInitParameter("api-class");   
  
    _homeAPI = loadClass(className);   
      }   
      else if (_homeImpl != null) {   
    _homeAPI = findRemoteAPI(_homeImpl.getClass());   
  
    if (_homeAPI == null)   
      _homeAPI = _homeImpl.getClass();   
      }   
         
      if (_objectImpl != null) {   
      }   
      else if (getInitParameter("object-class") != null) {   
    String className = getInitParameter("object-class");   
       
    Class objectClass = loadClass(className);   
  
    _objectImpl = objectClass.newInstance();   
  
    init(_objectImpl);   
      }   
  
      if (_objectAPI != null) {   
      }   
      else if (getInitParameter("object-api") != null) {   
    String className = getInitParameter("object-api");   
       
    _objectAPI = loadClass(className);   
      }   
      else if (_objectImpl != null)   
    _objectAPI = _objectImpl.getClass();   
  
      _homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI);   
      if (_objectAPI != null)   
    _homeSkeleton.setObjectClass(_objectAPI);   
  
      if (_objectImpl != null) {   
    _objectSkeleton = new HessianSkeleton(_objectImpl, _objectAPI);   
    _objectSkeleton.setHomeClass(_homeAPI);   
      }   
      else  
    _objectSkeleton = _homeSkeleton;   
    } catch (ServletException e) {   
      throw e;   
    } catch (Exception e) {   
      throw new ServletException(e);   
    }   
  }   
  
  private Class findRemoteAPI(Class implClass)   
  {   
    if (implClass == null || implClass.equals(GenericService.class))   
      return null;   
       
    Class []interfaces = implClass.getInterfaces();   
  
    if (interfaces.length == 1)   
      return interfaces[0];   
  
    return findRemoteAPI(implClass.getSuperclass());   
  }   
  
  private Class loadClass(String className)   
    throws ClassNotFoundException   
  {   
    ClassLoader loader = Thread.currentThread().getContextClassLoader();   
  
    if (loader != null)   
      return Class.forName(className, false, loader);   
    else  
      return Class.forName(className);   
  }   
  
  private void init(Object service)   
    throws ServletException   
  {   
    if (service instanceof Service)   
      ((Service) service).init(getServletConfig());   
    else if (service instanceof Servlet)   
      ((Servlet) service).init(getServletConfig());   
  }   
     
  /**  
   * Execute a request.  The path-info of the request selects the bean.  
   * Once the bean's selected, it will be applied.  
   */  
  public void service(ServletRequest request, ServletResponse response)   
    throws IOException, ServletException   
  {   
    HttpServletRequest req = (HttpServletRequest) request;   
    HttpServletResponse res = (HttpServletResponse) response;   
  
    if (! req.getMethod().equals("POST")) {   
      res.setStatus(500, "Hessian Requires POST");   
      PrintWriter out = res.getWriter();   
  
      res.setContentType("text/html");   
      out.println("<h1>Hessian Requires POST</h1>");   
         
      return;   
    }   
  
    String serviceId = req.getPathInfo();   
    String objectId = req.getParameter("id");   
    if (objectId == null)   
      objectId = req.getParameter("ejbid");   
  
    ServiceContext.begin(req, serviceId, objectId);   
  
    try {   
      InputStream is = request.getInputStream();   
      OutputStream os = response.getOutputStream();   
  
      Hessian2Input in = new Hessian2Input(is);   
      AbstractHessianOutput out;   
  
      SerializerFactory serializerFactory = getSerializerFactory();   
         
      in.setSerializerFactory(serializerFactory);   
  
      int code = in.read();   
  
      if (code != 'c') {   
    // XXX: deflate   
    throw new IOException("expected 'c' in hessian input at " + code);   
      }   
  
      int major = in.read();   
      int minor = in.read();   
  
      if (major >= 2)   
    out = new Hessian2Output(os);   
      else  
    out = new HessianOutput(os);   
         
      out.setSerializerFactory(serializerFactory);   
  
      if (objectId != null)   
    _objectSkeleton.invoke(in, out);   
      else  
    _homeSkeleton.invoke(in, out);   
  
      out.close();   
    } catch (RuntimeException e) {   
      throw e;   
    } catch (ServletException e) {   
      throw e;   
    } catch (Throwable e) {   
      throw new ServletException(e);   
    } finally {   
      ServiceContext.end();   
    }   
  }   
}  

/**
 * Servlet for serving Hessian services.
 */
public class HessianServlet extends GenericServlet {
  private Class _homeAPI;
  private Object _homeImpl;
  
  private Class _objectAPI;
  private Object _objectImpl;
  
  private HessianSkeleton _homeSkeleton;
  private HessianSkeleton _objectSkeleton;

  private SerializerFactory _serializerFactory;

  public String getServletInfo()
  {
    return "Hessian Servlet";
  }

  /**
   * Sets the home api.
   */
  public void setHomeAPI(Class api)
  {
    _homeAPI = api;
  }

  /**
   * Sets the home implementation
   */
  public void setHome(Object home)
  {
    _homeImpl = home;
  }

  /**
   * Sets the object api.
   */
  public void setObjectAPI(Class api)
  {
    _objectAPI = api;
  }

  /**
   * Sets the object implementation
   */
  public void setObject(Object object)
  {
    _objectImpl = object;
  }

  /**
   * Sets the service class.
   */
  public void setService(Object service)
  {
    setHome(service);
  }

  /**
   * Sets the api-class.
   */
  public void setAPIClass(Class api)
  {
    setHomeAPI(api);
  }

  /**
   * Gets the api-class.
   */
  public Class getAPIClass()
  {
    return _homeAPI;
  }

  /**
   * Sets the serializer factory.
   */
  public void setSerializerFactory(SerializerFactory factory)
  {
    _serializerFactory = factory;
  }

  /**
   * Gets the serializer factory.
   */
  public SerializerFactory getSerializerFactory()
  {
    if (_serializerFactory == null)
      _serializerFactory = new SerializerFactory();

    return _serializerFactory;
  }

  /**
   * Sets the serializer send collection java type.
   */
  public void setSendCollectionType(boolean sendType)
  {
    getSerializerFactory().setSendCollectionType(sendType);
  }

  /**
   * Initialize the service, including the service object.
   */
  public void init(ServletConfig config)
    throws ServletException
  {
    super.init(config);
    
    try {
      if (_homeImpl != null) {
      }
      else if (getInitParameter("home-class") != null) {
	String className = getInitParameter("home-class");
	
	Class homeClass = loadClass(className);

	_homeImpl = homeClass.newInstance();

	init(_homeImpl);
      }
      else if (getInitParameter("service-class") != null) {
	String className = getInitParameter("service-class");
	
	Class homeClass = loadClass(className);

	_homeImpl = homeClass.newInstance();
	
	init(_homeImpl);
      }
      else {
	if (getClass().equals(HessianServlet.class))
	  throw new ServletException("server must extend HessianServlet");

	_homeImpl = this;
      }

      if (_homeAPI != null) {
      }
      else if (getInitParameter("home-api") != null) {
	String className = getInitParameter("home-api");
	
	_homeAPI = loadClass(className);
      }
      else if (getInitParameter("api-class") != null) {
	String className = getInitParameter("api-class");

	_homeAPI = loadClass(className);
      }
      else if (_homeImpl != null) {
	_homeAPI = findRemoteAPI(_homeImpl.getClass());

	if (_homeAPI == null)
	  _homeAPI = _homeImpl.getClass();
      }
      
      if (_objectImpl != null) {
      }
      else if (getInitParameter("object-class") != null) {
	String className = getInitParameter("object-class");
	
	Class objectClass = loadClass(className);

	_objectImpl = objectClass.newInstance();

	init(_objectImpl);
      }

      if (_objectAPI != null) {
      }
      else if (getInitParameter("object-api") != null) {
	String className = getInitParameter("object-api");
	
	_objectAPI = loadClass(className);
      }
      else if (_objectImpl != null)
	_objectAPI = _objectImpl.getClass();

      _homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI);
      if (_objectAPI != null)
	_homeSkeleton.setObjectClass(_objectAPI);

      if (_objectImpl != null) {
	_objectSkeleton = new HessianSkeleton(_objectImpl, _objectAPI);
	_objectSkeleton.setHomeClass(_homeAPI);
      }
      else
	_objectSkeleton = _homeSkeleton;
    } catch (ServletException e) {
      throw e;
    } catch (Exception e) {
      throw new ServletException(e);
    }
  }

  private Class findRemoteAPI(Class implClass)
  {
    if (implClass == null || implClass.equals(GenericService.class))
      return null;
    
    Class []interfaces = implClass.getInterfaces();

    if (interfaces.length == 1)
      return interfaces[0];

    return findRemoteAPI(implClass.getSuperclass());
  }

  private Class loadClass(String className)
    throws ClassNotFoundException
  {
    ClassLoader loader = Thread.currentThread().getContextClassLoader();

    if (loader != null)
      return Class.forName(className, false, loader);
    else
      return Class.forName(className);
  }

  private void init(Object service)
    throws ServletException
  {
    if (service instanceof Service)
      ((Service) service).init(getServletConfig());
    else if (service instanceof Servlet)
      ((Servlet) service).init(getServletConfig());
  }
  
  /**
   * Execute a request.  The path-info of the request selects the bean.
   * Once the bean's selected, it will be applied.
   */
  public void service(ServletRequest request, ServletResponse response)
    throws IOException, ServletException
  {
    HttpServletRequest req = (HttpServletRequest) request;
    HttpServletResponse res = (HttpServletResponse) response;

    if (! req.getMethod().equals("POST")) {
      res.setStatus(500, "Hessian Requires POST");
      PrintWriter out = res.getWriter();

      res.setContentType("text/html");
      out.println("<h1>Hessian Requires POST</h1>");
      
      return;
    }

    String serviceId = req.getPathInfo();
    String objectId = req.getParameter("id");
    if (objectId == null)
      objectId = req.getParameter("ejbid");

    ServiceContext.begin(req, serviceId, objectId);

    try {
      InputStream is = request.getInputStream();
      OutputStream os = response.getOutputStream();

      Hessian2Input in = new Hessian2Input(is);
      AbstractHessianOutput out;

      SerializerFactory serializerFactory = getSerializerFactory();
      
      in.setSerializerFactory(serializerFactory);

      int code = in.read();

      if (code != 'c') {
	// XXX: deflate
	throw new IOException("expected 'c' in hessian input at " + code);
      }

      int major = in.read();
      int minor = in.read();

      if (major >= 2)
	out = new Hessian2Output(os);
      else
	out = new HessianOutput(os);
      
      out.setSerializerFactory(serializerFactory);

      if (objectId != null)
	_objectSkeleton.invoke(in, out);
      else
	_homeSkeleton.invoke(in, out);

      out.close();
    } catch (RuntimeException e) {
      throw e;
    } catch (ServletException e) {
      throw e;
    } catch (Throwable e) {
      throw new ServletException(e);
    } finally {
      ServiceContext.end();
    }
  }
}



先看init()函数,功能还是一样,初始话一些东西,读入init-param的内容,并且load这些init-param的class

主要的还是service()函数
在service函数里边会获得request和response对象的输入和输出流,用来构造Hessian2Input和Hessian2Output,Hessian就是解析这两个东西来执行函数调用的。当然,在service里边还有一个重要的语句
Java代码
ServiceContext.begin(req, serviceId, objectId); 

ServiceContext.begin(req, serviceId, objectId);
这个函数有点奇怪,我每次到这里serviceId和objectId都是空,不知道是不是历史遗留问题还存在这两个参数。
进去这个类看看
 
public class ServiceContext {   
  private static final ThreadLocal _localContext = new ThreadLocal();   
  
  private ServletRequest _request;   
  private String _serviceName;   
  private String _objectId;   
  private int _count;   
  private HashMap _headers = new HashMap();   
  
  private ServiceContext()   
  {   
  }   
     
  /**  
   * Sets the request object prior to calling the service's method.  
   *  
   * @param request the calling servlet request  
   * @param serviceId the service identifier  
   * @param objectId the object identifier  
   */  
  public static void begin(ServletRequest request,   
               String serviceName,   
               String objectId)   
    throws ServletException   
  {   
    ServiceContext context = (ServiceContext) _localContext.get();   
  
    if (context == null) {   
      context = new ServiceContext();   
      _localContext.set(context);   
    }   
  
    context._request = request;   
    context._serviceName = serviceName;   
    context._objectId = objectId;   
    context._count++;   
  }   
  
  /**  
   * Returns the service request.  
   */  
  public static ServiceContext getContext()   
  {   
    return (ServiceContext) _localContext.get();   
  }   
  
  /**  
   * Adds a header.  
   */  
  public void addHeader(String header, Object value)   
  {   
    _headers.put(header, value);   
  }   
  
  /**  
   * Gets a header.  
   */  
  public Object getHeader(String header)   
  {   
    return _headers.get(header);   
  }   
  
  /**  
   * Gets a header from the context.  
   */  
  public static Object getContextHeader(String header)   
  {   
    ServiceContext context = (ServiceContext) _localContext.get();   
  
    if (context != null)   
      return context.getHeader(header);   
    else  
      return null;   
  }   
  
  /**  
   * Returns the service request.  
   */  
  public static ServletRequest getContextRequest()   
  {   
    ServiceContext context = (ServiceContext) _localContext.get();   
  
    if (context != null)   
      return context._request;   
    else  
      return null;   
  }   
  
  /**  
   * Returns the service id, corresponding to the pathInfo of the URL.  
   */  
  public static String getContextServiceName()   
  {   
    ServiceContext context = (ServiceContext) _localContext.get();   
  
    if (context != null)   
      return context._serviceName;   
    else  
      return null;   
  }   
  
  /**  
   * Returns the object id, corresponding to the ?id= of the URL.  
   */  
  public static String getContextObjectId()   
  {   
    ServiceContext context = (ServiceContext) _localContext.get();   
  
    if (context != null)   
      return context._objectId;   
    else  
      return null;   
  }   
  
  /**  
   * Cleanup at the end of a request.  
   */  
  public static void end()   
  {   
    ServiceContext context = (ServiceContext) _localContext.get();   
  
    if (context != null && --context._count == 0) {   
      context._request = null;   
  
      context._headers.clear();   
    }   
  }   
  
  /**  
   * Returns the service request.  
   *  
   * @deprecated  
   */  
  public static ServletRequest getRequest()   
  {   
    ServiceContext context = (ServiceContext) _localContext.get();   
  
    if (context != null)   
      return context._request;   
    else  
      return null;   
  }   
  
  /**  
   * Returns the service id, corresponding to the pathInfo of the URL.  
   *  
   * @deprecated  
   */  
  public static String getServiceName()   
  {   
    ServiceContext context = (ServiceContext) _localContext.get();   
  
    if (context != null)   
      return context._serviceName;   
    else  
      return null;   
  }   
  
  /**  
   * Returns the object id, corresponding to the ?id= of the URL.  
   *  
   * @deprecated  
   */  
  public static String getObjectId()   
  {   
    ServiceContext context = (ServiceContext) _localContext.get();   
  
    if (context != null)   
      return context._objectId;   
    else  
      return null;   
  }   
}  

public class ServiceContext {
  private static final ThreadLocal _localContext = new ThreadLocal();

  private ServletRequest _request;
  private String _serviceName;
  private String _objectId;
  private int _count;
  private HashMap _headers = new HashMap();

  private ServiceContext()
  {
  }
  
  /**
   * Sets the request object prior to calling the service's method.
   *
   * @param request the calling servlet request
   * @param serviceId the service identifier
   * @param objectId the object identifier
   */
  public static void begin(ServletRequest request,
			   String serviceName,
			   String objectId)
    throws ServletException
  {
    ServiceContext context = (ServiceContext) _localContext.get();

    if (context == null) {
      context = new ServiceContext();
      _localContext.set(context);
    }

    context._request = request;
    context._serviceName = serviceName;
    context._objectId = objectId;
    context._count++;
  }

  /**
   * Returns the service request.
   */
  public static ServiceContext getContext()
  {
    return (ServiceContext) _localContext.get();
  }

  /**
   * Adds a header.
   */
  public void addHeader(String header, Object value)
  {
    _headers.put(header, value);
  }

  /**
   * Gets a header.
   */
  public Object getHeader(String header)
  {
    return _headers.get(header);
  }

  /**
   * Gets a header from the context.
   */
  public static Object getContextHeader(String header)
  {
    ServiceContext context = (ServiceContext) _localContext.get();

    if (context != null)
      return context.getHeader(header);
    else
      return null;
  }

  /**
   * Returns the service request.
   */
  public static ServletRequest getContextRequest()
  {
    ServiceContext context = (ServiceContext) _localContext.get();

    if (context != null)
      return context._request;
    else
      return null;
  }

  /**
   * Returns the service id, corresponding to the pathInfo of the URL.
   */
  public static String getContextServiceName()
  {
    ServiceContext context = (ServiceContext) _localContext.get();

    if (context != null)
      return context._serviceName;
    else
      return null;
  }

  /**
   * Returns the object id, corresponding to the ?id= of the URL.
   */
  public static String getContextObjectId()
  {
    ServiceContext context = (ServiceContext) _localContext.get();

    if (context != null)
      return context._objectId;
    else
      return null;
  }

  /**
   * Cleanup at the end of a request.
   */
  public static void end()
  {
    ServiceContext context = (ServiceContext) _localContext.get();

    if (context != null && --context._count == 0) {
      context._request = null;

      context._headers.clear();
    }
  }

  /**
   * Returns the service request.
   *
   * @deprecated
   */
  public static ServletRequest getRequest()
  {
    ServiceContext context = (ServiceContext) _localContext.get();

    if (context != null)
      return context._request;
    else
      return null;
  }

  /**
   * Returns the service id, corresponding to the pathInfo of the URL.
   *
   * @deprecated
   */
  public static String getServiceName()
  {
    ServiceContext context = (ServiceContext) _localContext.get();

    if (context != null)
      return context._serviceName;
    else
      return null;
  }

  /**
   * Returns the object id, corresponding to the ?id= of the URL.
   *
   * @deprecated
   */
  public static String getObjectId()
  {
    ServiceContext context = (ServiceContext) _localContext.get();

    if (context != null)
      return context._objectId;
    else
      return null;
  }
}


原来ServiceContext 是用来保存当前调用线程的上下文的,比如request对象等(不知道这个解释对不对)。有了这个东西就太好了,因为里边有request,就有了调用端的一切信息,呵呵。

继续回来看那个Servlet,到了真正调用的时候了,也就是这段代码


if (objectId != null)   
ctSkeleton.invoke(in, out);   
else  
Skeleton.invoke(in, out);  

      if (objectId != null)
	_objectSkeleton.invoke(in, out);
      else
	_homeSkeleton.invoke(in, out);


跟踪invoke方法看看真面目

 
public void invoke(AbstractHessianInput in, AbstractHessianOutput out)   
  throws Throwable   
{   
  ServiceContext context = ServiceContext.getContext();   
     
  String header;   
  while ((header = in.readHeader()) != null) {   
    Object value = in.readObject();   
  
    context.addHeader(header, value);   
  }   
  String ip = context.getContextRequest().getRemoteAddr();   
  String methodName = in.readMethod();   
  Method method = getMethod(methodName);   
  
  if (method != null) {   
  }   
  else if ("_hessian_getAttribute".equals(methodName)) {   
    String attrName = in.readString();   
    in.completeCall();   
  
    String value = null;   
  
    if ("java.api.class".equals(attrName))   
alue = getAPIClassName();   
    else if ("java.home.class".equals(attrName))   
alue = getHomeClassName();   
    else if ("java.object.class".equals(attrName))   
alue = getObjectClassName();   
  
    out.startReply();   
  
    out.writeObject(value);   
  
    out.completeReply();   
    return;   
  }   
  else if (method == null) {   
    out.startReply();   
    out.writeFault("NoSuchMethodException",   
     "The service has no method named: " + in.getMethod(),   
     null);   
    out.completeReply();   
    return;   
  }   
  
  Class []args = method.getParameterTypes();   
  Object []values = new Object[args.length];   
     
  //args[0]   
  
  for (int i = 0; i < args.length; i++){   
    if(i == args.length-1){   
        values[i] = in.readObject(args[i], ip);   
    }else{   
        values[i] = in.readObject(args[i]);   
    }   
       
  }   
       
  
  in.completeCall();   
  
  Object result = null;   
     
  try {   
    result = method.invoke(_service, values);   
  } catch (Throwable e) {   
    if (e instanceof InvocationTargetException)   
      e = ((InvocationTargetException) e).getTargetException();   
  
    log.log(Level.WARNING, e.toString(), e);   
       
    out.startReply();   
    out.writeFault("ServiceException", e.getMessage(), e);   
    out.completeReply();   
    return;   
  }   
  
  out.startReply();   
  
  out.writeObject(result);   
     
  out.completeReply();   
}  

  public void invoke(AbstractHessianInput in, AbstractHessianOutput out)
    throws Throwable
  {
    ServiceContext context = ServiceContext.getContext();
    
    String header;
    while ((header = in.readHeader()) != null) {
      Object value = in.readObject();

      context.addHeader(header, value);
    }
    String ip = context.getContextRequest().getRemoteAddr();
    String methodName = in.readMethod();
    Method method = getMethod(methodName);

    if (method != null) {
    }
    else if ("_hessian_getAttribute".equals(methodName)) {
      String attrName = in.readString();
      in.completeCall();

      String value = null;

      if ("java.api.class".equals(attrName))
	value = getAPIClassName();
      else if ("java.home.class".equals(attrName))
	value = getHomeClassName();
      else if ("java.object.class".equals(attrName))
	value = getObjectClassName();

      out.startReply();

      out.writeObject(value);

      out.completeReply();
      return;
    }
    else if (method == null) {
      out.startReply();
      out.writeFault("NoSuchMethodException",
		     "The service has no method named: " + in.getMethod(),
		     null);
      out.completeReply();
      return;
    }

    Class []args = method.getParameterTypes();
    Object []values = new Object[args.length];
    
    //args[0]

    for (int i = 0; i < args.length; i++){
    	if(i == args.length-1){
    		values[i] = in.readObject(args[i], ip);
    	}else{
    		values[i] = in.readObject(args[i]);
    	}
    	
    }
      

    in.completeCall();

    Object result = null;
    
    try {
      result = method.invoke(_service, values);
    } catch (Throwable e) {
      if (e instanceof InvocationTargetException)
        e = ((InvocationTargetException) e).getTargetException();

      log.log(Level.WARNING, e.toString(), e);
      
      out.startReply();
      out.writeFault("ServiceException", e.getMessage(), e);
      out.completeReply();
      return;
    }

    out.startReply();

    out.writeObject(result);
    
    out.completeReply();
  }


就是在这个方法里边,hessian把包装过的输入输出流当作参数传入并进行解析的,看看这个函数的第一句,正是取得ServiceContext的地方,此时应该就是把刚才Servlet里边保存的上下文取出来使用。
这个时候出现了第一个hack的地方 Java代码
String ip = context.getContextRequest().getRemoteAddr(); 

String ip = context.getContextRequest().getRemoteAddr();在此处我取得远程的ip地址保存起来。然后在第二个hack的地方

Class []args = method.getParameterTypes();   
Object []values = new Object[args.length];   
  
//args[0]   
  
for (int i = 0; i < args.length; i++){   
    if(i == args.length-1){   
        values[i] = in.readObject(args[i], ip);   
    }else{   
        values[i] = in.readObject(args[i]);   
    }   
       
}  

    Class []args = method.getParameterTypes();
    Object []values = new Object[args.length];
    
    //args[0]

    for (int i = 0; i < args.length; i++){
    	if(i == args.length-1){
    		values[i] = in.readObject(args[i], ip);
    	}else{
    		values[i] = in.readObject(args[i]);
    	}
    	
    }
我用这个ip地址取代最后一个参数(web服务函数的参数,即远程端调用的函数的参数)。
第三个hack的地方就是 in.readObject(args[i], ip); 这个方法。 这个方法是我自己加的,原本只有
in.readObject(args[i]); 这个方法。 这个方法就是hessian读取参数值的地方
进去看看

/**  
 * Reads an object from the input stream with an expected type.  
 */  
public Object readObject(Class cl, String ip)   
  throws IOException   
{   
  if (cl == null || cl == Object.class)   
    return readObject();   
     
  int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();   
  
  switch (tag) {   
  case 'N':   
    return null;   
  
  case 'M':   
  {   
    String type = readType();   
    Deserializer reader;   
    reader = findSerializerFactory().getObjectDeserializer(type);   
  
    if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))   
      return reader.readMap(this);   
  
    reader = findSerializerFactory().getDeserializer(cl);   
  
    return reader.readMap(this);   
  }   
  
  case 'O':   
  {   
    return readObjectDefinition(cl);   
  }   
  
  case 'o':   
  {   
    int ref = readInt();   
  
    ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref - 1);   
  
    return readObjectInstance(cl, def);   
  }   
  
  case 'V':   
  {   
    String type = readType();   
    int length = readLength();   
       
    Deserializer reader;   
    reader = findSerializerFactory().getObjectDeserializer(type);   
       
    if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))   
      return reader.readList(this, length);   
  
    reader = findSerializerFactory().getDeserializer(cl);   
  
    Object v = reader.readList(this, length);   
  
    return v;   
  }   
  
  case 'v':   
  {   
    int ref = readInt();   
    String type = (String) _types.get(ref);   
    int length = readInt();   
       
    Deserializer reader;   
    reader = findSerializerFactory().getObjectDeserializer(type);   
       
    if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))   
      return reader.readLengthList(this, length);   
  
    reader = findSerializerFactory().getDeserializer(cl);   
  
    Object v = reader.readLengthList(this, length);   
  
    return v;   
  }   
  
  case 'R':   
  {   
    int ref = parseInt();   
  
    return _refs.get(ref);   
  }   
  
  case 'r':   
  {   
    String type = readType();   
    String url = readString();   
  
    return resolveRemote(type, url);   
  }   
  }   
  
  if (tag >= 0)   
    _offset--;   
  
  Object value = findSerializerFactory().getDeserializer(cl).readObject(this);   
  
  if(value instanceof String){   
    value = ip;   
  }   
  return value;   
}  

  /**
   * Reads an object from the input stream with an expected type.
   */
  public Object readObject(Class cl, String ip)
    throws IOException
  {
    if (cl == null || cl == Object.class)
      return readObject();
    
    int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();

    switch (tag) {
    case 'N':
      return null;

    case 'M':
    {
      String type = readType();
      Deserializer reader;
      reader = findSerializerFactory().getObjectDeserializer(type);

      if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))
        return reader.readMap(this);

      reader = findSerializerFactory().getDeserializer(cl);

      return reader.readMap(this);
    }

    case 'O':
    {
      return readObjectDefinition(cl);
    }

    case 'o':
    {
      int ref = readInt();

      ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref - 1);

      return readObjectInstance(cl, def);
    }

    case 'V':
    {
      String type = readType();
      int length = readLength();
      
      Deserializer reader;
      reader = findSerializerFactory().getObjectDeserializer(type);
      
      if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))
        return reader.readList(this, length);

      reader = findSerializerFactory().getDeserializer(cl);

      Object v = reader.readList(this, length);

      return v;
    }

    case 'v':
    {
      int ref = readInt();
      String type = (String) _types.get(ref);
      int length = readInt();
      
      Deserializer reader;
      reader = findSerializerFactory().getObjectDeserializer(type);
      
      if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))
        return reader.readLengthList(this, length);

      reader = findSerializerFactory().getDeserializer(cl);

      Object v = reader.readLengthList(this, length);

      return v;
    }

    case 'R':
    {
      int ref = parseInt();

      return _refs.get(ref);
    }

    case 'r':
    {
      String type = readType();
      String url = readString();

      return resolveRemote(type, url);
    }
    }

    if (tag >= 0)
      _offset--;

    Object value = findSerializerFactory().getDeserializer(cl).readObject(this);

    if(value instanceof String){
    	value = ip;
    }
    return value;
  }
我重载了这个方法,加入了一个String类型的参数,用来把ip地址传进去,并且最后返回这个值。到了这里,hack的原理大家应该知道了--就是强行修改远程调用端的调用函数里边的最后一个参数的值(规定为String类型),把这个值设为我想要的信息,那么服务端的服务函数就会获得这个值,并且进行后续处理。
剩下的步骤就原封不动的是hessian来处理了,没有需要干涉的地方,你也就能在你的服务端service函数里边获得这个你想要的信息了。

这就是Hessian的一个普通流程,不知道分析和Hack的对不对,我在这里是调试成功了,但是还没彻底测试有没有其它bug。 至于跟Spring的结合,待会儿跟帖来说。
分享到:
评论

相关推荐

    hessian-lite-3.2.1-fixed-2.jar

    com.alibaba:hessian-lite:jar:3.2.1-fixed-2 hessian-lite hessian-lite-3.2.1-fixed-2.jar

    Hessian源码分析和Hack.doc

    《Hessian源码分析与Hack:携带远程调用端信息》 Hessian作为一种轻量级的RPC(远程过程调用)框架,因其高效、简洁的二进制协议,被广泛应用于构建Web服务。然而,在实际应用中,有时我们需要获取到远程调用端的IP...

    hessian-4.0.63-API文档-中文版.zip

    赠送Maven依赖信息文件:hessian-4.0.63.pom; 包含翻译后的API文档:hessian-4.0.63-javadoc-API文档-中文(简体)版.zip; Maven坐标:com.caucho:hessian:4.0.63; 标签:hessian、caucho、jar包、java、中文文档;...

    hessian-4.0.63-API文档-中英对照版.zip

    赠送Maven依赖信息文件:hessian-4.0.63.pom; 包含翻译后的API文档:hessian-4.0.63-javadoc-API文档-中文(简体)-英语-对照版.zip; Maven坐标:com.caucho:hessian:4.0.63; 标签:hessian、caucho、jar包、java、...

    hessian-4.0.51-src

    在解压的"**hessian-4.0.51-src**"源码包中,主要包含两个关键文件:"**pom.xml**"和"**src**"。 1. **pom.xml**:这是Maven项目的配置文件,定义了项目依赖、构建过程等信息。通过这个文件,我们可以看到Hessian ...

    hessian-3.3.6-API文档-中英对照版.zip

    赠送jar包:hessian-3.3.6.jar 赠送原API文档:hessian-3.3.6-javadoc.jar 赠送源代码:hessian-3.3.6-sources.jar 包含翻译后的API文档:hessian-3.3.6-javadoc-API文档-中文(简体)-英语-对照版.zip 对应Maven...

    hessian实现远程调用

    在IT行业中,远程调用是一种常见的技术,使得客户端可以跨网络调用远程服务器上的方法,就像调用本地方法一样方便。Hessian是Apache项目下的一个轻量级的RPC(Remote Procedure Call,远程过程调用)框架,它提供了...

    hessian-3.3.6-API文档-中文版.zip

    赠送Maven依赖信息文件:hessian-3.3.6.pom; 包含翻译后的API文档:hessian-3.3.6-javadoc-API文档-中文(简体)版.zip; Maven坐标:com.alipay.sofa:hessian:3.3.6; 标签:sofa、hessian、alipay、jar包、java、...

    hessian-3.0.20-src.jar

    hessian是一个轻量级的Java Remoting方案

    Hessian远程调用框架学习一

    Hessian是由Caucho Technology开发的一种二进制Web服务协议,它提供了对Java对象序列化和反序列化的支持,使得远程调用如同本地调用一样快速和简单。与传统的XML-RPC或SOAP相比,Hessian的序列化格式更为紧凑,因此...

    Hessian-4.0.7(Jar包 + 源码)

    Hessian是一种轻量级的二进制Web服务协议,它由Caucho Technology开发,用于提高远程调用的效率和速度。Hessian 4.0.7是该协议的一个版本,提供了一个Java库,允许开发者在Java应用之间进行高效的数据交换。这个版本...

    Hessian多个版本打包下载

    Hessian是一种高效的二进制序列化协议,常用于远程过程调用(RPC)和服务之间的通信。这个压缩包包含了Hessian的多个版本,分别是Hessian3.1.6、Hessian3.2.1以及Hessian4.0.7。每个版本都有其特定的功能改进和优化...

    hessian-lite-3.2.1-fixed-2-sources.jar

    java运行依赖jar包

    轻量级远程服务调用Hessian的入门实例和与Spring整合的实例.zip

    在服务器端,通过HessianServlet来暴露这个服务,客户端则通过HessianProxyFactory来创建服务代理,从而能够调用远程服务的方法。这种简单设置可以让开发者快速理解Hessian的基本工作原理。 二、Hessian与Spring...

    hessian-4.0.33.jar

    在这个专题中,我们将以hessian-4.0.33.jar为例,详细探讨Hessian框架的核心特性和使用方法。 一、Hessian协议简介 Hessian协议基于HTTP协议,但其数据传输采用二进制格式,这使得它在数据传输效率上远超基于文本...

    hessian-lite

    1. **远程调用**:在Dubbo框架中,Hessian-lite用于实现服务调用的二进制序列化和反序列化,使得远程方法调用(RMI)更加高效。它将Java对象转换为二进制流,通过网络发送,然后在服务端反序列化回原来的对象,降低了...

    dubbo-hessian-lite

    【标题】"dubbo-hessian-lite" 是一个与阿里巴巴的著名开源远程调用框架 Dubbo 相关的组件。Dubbo 提供了多种序列化方式,其中之一就是 Hessian 序列化,而 hessian-lite 是 Dubbo 在编译时依赖的一个轻量级 Hessian...

    spring整合hessian进行远程通讯

    Spring会自动创建代理对象,使得客户端可以通过这个代理对象调用远程服务的方法,就像调用本地对象一样。 3. **使用源码分析**: Hessian库提供了`HessianProxyFactoryBean`和`HessianServiceExporter`这两个关键...

    Hessian Binary Web Service Protocol远程接口调用入门Demo

    Hessian二进制Web服务协议(Hessian Binary Web Service Protocol)是一种高效的、轻量级的远程过程调用(RPC)协议,它主要用于提高Web服务之间的通信效率。Hessian由Caucho Technology公司开发,旨在解决XML-RPC在...

Global site tag (gtag.js) - Google Analytics