Announcement Announcement Module
Collapse
No announcement yet.
Proxy Workaround for M3 Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Proxy Workaround for M3

    It took me hours to figure out (I'm not even sure if I would call this a progress) to make Spring Social work behind our proxy.

    The main problem is there's no easy way to setup the proxy. A direct call to HttpClient is entirely impossible via plain XML config. (Maybe even via Java-config?)

    Basically the idea is to modify the RequestFactory that is passed to RestTemplate's constructor. So that it contains the proxy settings:
    Code:
    		SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
    		Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("my.company.org", 9999));
    		requestFactory.setProxy(proxy);
    		RestTemplate restTemplate = new RestTemplate(requestFactory);
    The workaround is two parts:

    First, I had to reimplement FacebookOAuth2Template createRestTemplate() method:

    Code:
    @Override
    	protected RestTemplate createRestTemplate() {
    		SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
    		Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("my.company.org", 9999));
    		requestFactory.setProxy(proxy);
    		RestTemplate restTemplate = new RestTemplate(requestFactory);
    		FormHttpMessageConverter messageConverter = new FormHttpMessageConverter() {
    			public boolean canRead(Class<?> clazz, MediaType mediaType) {
    				// always read as x-www-url-formencoded even though Facebook sets contentType to text/plain				
    				return true;
    			}
    		};
    		restTemplate.setMessageConverters(Collections.<HttpMessageConverter<?>>singletonList(messageConverter));
    		return restTemplate;
    	}
    Literally, I created a new class and copied the whole implementation of FacebookOAuth2Template and renamed the new class as ModifiedFacebookOAuth2Template.

    This works on the authentication part to Facebook. I can see clearly in the userconnection table that the Facebook connection has been created. However, when I try to view my profile and post to my wall, I get a timeout error. Again, it's because the proxy is not setup though I already did!

    It turns out I also have to modify the RequestFactory that's declared inside the FacebookTemplate. This is the second part.

    FacebookTemplate calls the following factory class to initialize its RestTemplate:

    Code:
    	this.restTemplate = ProtectedResourceClientFactory.draft10(accessToken);
    So basically I had to modify ProtectedResourceClientFactory and re-implemented it like the following:
    Code:
    private static RestTemplate version(String accessToken, OAuth2Version version) {
    
    		SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
    		Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("my.company.org", 9999));
    		requestFactory.setProxy(proxy);
    		RestTemplate client = new RestTemplate(requestFactory);
    		if (interceptorsSupported) {
    			// favored
    			client.setInterceptors(new ClientHttpRequestInterceptor[] { new OAuth2RequestInterceptor(accessToken, version) });
    		} else {
    			// 3.0.x compatibility
    			client.setRequestFactory(new Spring30OAuth2RequestFactory(client.getRequestFactory(), accessToken, version));
    		}
    		return client;				
    	}
    But then in order for this to work, I had to literally copy 20+ classes and modify their visibility to public so that my custom package is able to see them! For example, EventTemplate, CommentList, AlbumList, ReferenceList, VideoList, OAuth2Version, and so forth.

    It's too lengthy and takes a lot of work. The visibility of these classes made the modification tedious.

    My application works, but I believe there should be a simpler way to set this up. I tried adding Apache Commons HttpClient 4.1 in the classpath and see if I can modify it's parameters so that the proxy settings are assingned to. Unfortunately, the HttpClient is created runtime and not via configuration.

    I assume it would also work if I copy and modify the classes that contain HttpClient but that would even require more classes to edit.

    Or maybe there's an easy that I've missed?

  • #2
    You're right in saying that there is no easy way to configure Spring Social to be used behind a proxy. But this should definitely be addressed. Therefore, I've created https://jira.springsource.org/browse/SOCIAL-146 to track this issue and will be working to resolve it.

    Comment


    • #3
      Thanks for confirming it. At least this gives me comfort that I haven't missed a thing Thanks for opening-up a JIRA as well. I appreciate it.

      Comment


      • #4
        We just landed a patch that allows the RequestFactory to be configured against each API binding. It's available now in the latest snapshot. Let us know if that meets your needs!

        Comment


        • #5
          The latest snapshot builds include the ability to specify the proxy host/port using the "http.proxyHost" and "http.proxyPort" system properties (commonly set on the JVM via -Dhttp.proxyHost=somehost -Dhttp.proxyPort=8080). This works without the need to configure a custom request factory. Could you try this out and let us know if it works for you and meets your needs? Thanks!

          Comment


          • #6
            That's nice to know. I will certainly try it later. I will have to be at work to test it I've just woke-up (6am here).

            Comment


            • #7
              Great! It works. I had three hiccups but I was able to resolve them:

              First, I had to setup the correct repositories for the snapshot. I found your post at http://forum.springsource.org/showth...book-social-M2

              For reference, I used the following pom.xml configuration:

              Code:
                                      
                      <properties>
              		...
              		<spring.social.version>1.0.0.BUILD-SNAPSHOT</spring.social.version>
              	</properties>
              
              	...
              
              	<repository>
              		<id>org.springframework.maven.release</id>
              		<name>Spring Maven Release Repository</name>
              		<url>http://maven.springframework.org/release</url>
              		<releases>
              			<enabled>true</enabled>
              		</releases>
              		<snapshots>
              			<enabled>false</enabled>
              		</snapshots>
              	</repository>
              	<!-- For testing against latest Spring snapshots -->
              	<repository>
              		<id>org.springframework.maven.snapshot</id>
              		<name>Spring Maven Snapshot Repository</name>
              		<url>http://maven.springframework.org/snapshot</url>
              		<releases>
              			<enabled>false</enabled>
              		</releases>
              		<snapshots>
              			<enabled>true</enabled>
              		</snapshots>
              	</repository>
              	<!-- For developing against latest Spring milestones -->
              	<repository>
              		<id>org.springframework.maven.milestone</id>
              		<name>Spring Maven Milestone Repository</name>
              		<url>http://maven.springframework.org/milestone</url>
              		<snapshots>
              			<enabled>false</enabled>
              		</snapshots>
              	</repository>

              Second, I had to find an elegant way to inject the proxy settings, and found the following (See http://stackoverflow.com/questions/3...iguration-file)

              Code:
               
              	<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
              	    <property name="targetObject">
              	        <!-- System.getProperties() -->
              	        <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
              	            <property name="targetClass" value="java.lang.System" />
              	            <property name="targetMethod" value="getProperties" />
              	        </bean>
              	    </property>
              	    <property name="targetMethod" value="putAll" />
              	    <property name="arguments">
              	        <!-- The new Properties -->
              	        <util:properties>
              	            <prop key="http.proxyHost">xxxxxx</prop>
              	            <prop key="http.proxyPort">1111</prop>
              	        </util:properties>
              	    </property>
              	</bean>
              Third, I have to rename FacebookApi to Facebook.

              Everything is working fine in relation to the proxy issue. I'm gonna continue my project now Thanks a lot

              Comment


              • #8
                Following-up, I was able to simplify the setup of the proxy settings using SpEL as indicated in the comments in the stackoverflow.com post:

                Code:
                <!-- http://stackoverflow.com/questions/3339736/set-system-property-with-spring-configuration-file -->
                	<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
                	    <property name="targetObject" value="#{@systemProperties}" />
                	    <property name="targetMethod" value="putAll" />
                	    <property name="arguments">
                	        <!-- The new Properties -->
                	        <util:properties>
                	            <prop key="http.proxyHost">xxxxxxx</prop>
                	            <prop key="http.proxyPort">1111</prop>
                	        </util:properties>
                	    </property>
                	</bean>

                Comment


                • #9
                  I tried the latest snapshot. For some reason the proxy isn't working again. I'm getting connection timeout. The same exact behavior when I had problems with the proxy settings.

                  The only difference I see from the previous snapshot (the one that worked) and today's snapshot is the addition of a private HttpComponentsClientRequestFactoryCreator inside the ClientHttpRequestFactorySelector.

                  Snapshot that was still working:
                  https://github.com/SpringSource/spri...0c6156128ec804

                  Updated snapshot that somehow broke the proxy functionality:
                  https://github.com/SpringSource/spri...8d47d2b778f6fa https://github.com/SpringSource/spri...ec6c250d3c73d4

                  Comment


                  • #10
                    I've enabled DEBUG logging and here's what I got:

                    Code:
                    Caused by: org.apache.commons.httpclient.ConnectTimeoutException: The host did not accept the connection within timeout of 3000 ms
                    	at org.apache.commons.httpclient.protocol.ReflectionSocketFactory.createSocket(ReflectionSocketFactory.java:155)
                    	at org.apache.commons.httpclient.protocol.SSLProtocolSocketFactory.createSocket(SSLProtocolSocketFactory.java:130)
                    	at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707)
                    	at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager$HttpConnectionAdapter.open(MultiThreadedHttpConnectionManager.java:1361)
                    	at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:387)
                    	at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
                    	at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
                    	at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
                    	at org.openid4java.util.HttpCache.head(HttpCache.java:296)
                    	at org.openid4java.discovery.yadis.YadisResolver.retrieveXrdsLocation(YadisResolver.java:360)
                    	... 33 more
                    Caused by: java.net.SocketTimeoutException: connect timed out
                    	at java.net.PlainSocketImpl.socketConnect(Native Method)
                    	at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
                    	at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
                    	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
                    	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
                    	at java.net.Socket.connect(Socket.java:519)
                    	at com.sun.net.ssl.internal.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:550)
                    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                    	at java.lang.reflect.Method.invoke(Method.java:597)
                    	at org.apache.commons.httpclient.protocol.ReflectionSocketFactory.createSocket(ReflectionSocketFactory.java:140)
                    	... 42 more
                    Apparently, I can still connect to Facebook with proxy enabled. However, the problem seems to manifest when I implement an OpenID login using Spring Security 3.1.0.RC1. This only started when I used the latest spring-social snapshot. Yesterday, everything still works. In fact, I was able to upload my app to CloudFoundry.

                    Comment


                    • #11
                      Definitely it's a proxy issue. I've uploaded right now the latest build of my sample app to CloudFoundry and I was able to login via OpenID (proxy is turned off). I've updated the HttpClient from 3.1 to 4.1.1 but it didn't make any difference. (Spring Security 3.1.0.RC1 has dependencies to HttpClient 3.1)

                      Comment


                      • #12
                        Is the problem limited to your OpenID connections, and your Spring Social connections work fine with proxy settings picked up correctly? As you mentioned, we haven't made any changes there other than isolating the HttpComponents 4.x class imports in a inner class to account for eager class verification environments when Http Components 4.x is not on the classpath. The proxy configuration we apply in ClientHttpRequestFactorySelector is local to Spring Social usages (OAuthOperations and API bindings), and is not picked up anywhere else. So I don't think Spring Social could effect Spring Security in this area.

                        You may want to consider ruling out Spring Social as the culprit by turning on your debugger inside ClientHttpRequestFactorySelector and verifying proxyHost/proxyPort is set properly.

                        Keith

                        Comment


                        • #13
                          Sadly, it's Friday night here and no work That means I have no access to an environment with proxy. It will take until Monday before I can confirm any. But what I noticed is that the Spring Social does indeed pickup the proxy settings. I'm able to retrieve the proxy values and able to login and connect to Facebook via Spring Social. So I know that works.

                          It appears the problem is with the Spring Security's dependency to HttpClient 3.1. The proxy values are not being picked up. It started appearing right after I used the Spring Social snapshot builds. But again, I can't confirm until Monday. For the meantime I'm gonna work on some other parts of my app. By the way the app I'm doing is a scheduler that allows you to post to schedule messages to social networks. And it's based on the following:

                          - Spring 3.1.0.M1
                          - Spring Security 3.1.0.RC2
                          - Spring Social 1.0.0.M3
                          - Spring JPA 1.0.0.M2
                          - Spring Scheduler
                          - jQuery, jqGrid

                          And it's deployed to CloudFoundry.

                          Comment


                          • #14
                            You might want to check with Spring Security team about their dependency on Http Client 3.1 and see if it can be upgraded to 4.x. Spring Framework 3.1 now has HttpComponents 4.x support and 3.x really is old school at this point. The two libraries are entirely independent of one another so there should be no conflict or other relation to them. You'd have to check with v3.1 and Spring Security to see how to customize proxy settings there ... what we're doing (applying http.proxyHost & http.proxyPort System.properties) in Spring Social for both standard J2SE and HttpComponents 4 clients is nice and convenient, but it's just limited to our library and what we can control.

                            Your scheduler app sounds cool btw. Looking forward to trying it out.

                            Keith
                            Last edited by Keith Donald; May 13th, 2011, 10:30 AM.

                            Comment


                            • #15
                              I spoke to Luke Taylor (Spring Security Lead) on this issue. Luke indicates that the OpenID integration does not depend on HttpClient 3.1; rather, it depends on 4.1. See below:

                              Code:
                              Luke Taylor 10:33 AM 
                              If I build our OpenID sample, I get:
                              WEB-INF/lib/guice-2.0.jar
                              WEB-INF/lib/httpclient-4.1.1.jar
                              WEB-INF/lib/httpcore-4.1.jar
                              WEB-INF/lib/commons-codec-1.4.jar
                              ... in the jar...
                              
                              Keith Donald 10:33 AM 
                              Do you know how he can apply proxy settings there for that module?
                              
                              Luke Taylor 10:34 AM 
                              No. I guess he'd have to configure the http client implementation used by OpenID4Java.  Perhaps check the OpenID4Java docs?
                              You might want to inspect your Maven dependency hierarchy and see who is pulling down that HttpClient 3.1 dependency. According to Luke, Spring Security should not be. If it is, it is a bug.

                              Keith

                              Comment

                              Working...
                              X