Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
O
Openfire
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Openfire
Commits
74ed3b77
Commit
74ed3b77
authored
Feb 27, 2015
by
Guus der Kinderen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
OF-885: Openfire should make use of the async servlet API.
parent
194e49e4
Changes
5
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
393 additions
and
491 deletions
+393
-491
HttpBindServlet.java
src/java/org/jivesoftware/openfire/http/HttpBindServlet.java
+202
-231
HttpBindTimeoutException.java
.../jivesoftware/openfire/http/HttpBindTimeoutException.java
+0
-37
HttpConnection.java
src/java/org/jivesoftware/openfire/http/HttpConnection.java
+20
-88
HttpSession.java
src/java/org/jivesoftware/openfire/http/HttpSession.java
+154
-57
HttpSessionManager.java
...va/org/jivesoftware/openfire/http/HttpSessionManager.java
+17
-78
No files found.
src/java/org/jivesoftware/openfire/http/HttpBindServlet.java
View file @
74ed3b77
This diff is collapsed.
Click to expand it.
src/java/org/jivesoftware/openfire/http/HttpBindTimeoutException.java
deleted
100644 → 0
View file @
194e49e4
/**
* $RCSfile$
* $Revision: $
* $Date: $
*
* Copyright (C) 2005-2008 Jive Software. All rights reserved.
*
* Licensed 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
.
jivesoftware
.
openfire
.
http
;
/**
* An exception which indicates that the maximum waiting time for a client response has been
* surpassed and an empty response should be returned to the requesting client.
*
* @author Alexander Wenckus
*/
class
HttpBindTimeoutException
extends
Exception
{
public
HttpBindTimeoutException
(
String
message
)
{
super
(
message
);
}
public
HttpBindTimeoutException
()
{
super
();
}
}
src/java/org/jivesoftware/openfire/http/HttpConnection.java
View file @
74ed3b77
...
...
@@ -20,17 +20,16 @@
package
org
.
jivesoftware
.
openfire
.
http
;
import
org.jivesoftware.util.JiveConstants
;
import
org.eclipse.jetty.continuation.Continuation
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
javax.servlet.AsyncContext
;
import
java.io.IOException
;
import
java.security.cert.X509Certificate
;
/**
* Represents one HTTP connection with a client using the HTTP Binding service. The client will wait
* on
{@link #getResponse()}
until the server forwards a message to it or the wait time on the
* on
a response
until the server forwards a message to it or the wait time on the
* session timeout.
*
* @author Alexander Wenckus
...
...
@@ -38,18 +37,16 @@ import java.security.cert.X509Certificate;
public
class
HttpConnection
{
private
static
final
Logger
Log
=
LoggerFactory
.
getLogger
(
HttpConnection
.
class
);
private
static
final
String
RESPONSE_BODY
=
"response-body"
;
private
static
final
String
CONNECTION_CLOSED
=
"connection closed"
;
private
final
long
requestId
;
private
final
X509Certificate
[]
sslCertificates
;
private
final
boolean
isSecure
;
private
String
body
;
private
HttpSession
session
;
private
Continuation
continuation
;
private
boolean
isClosed
;
private
final
AsyncContext
context
;
/**
* Constructs an HTTP Connection.
*
...
...
@@ -57,25 +54,30 @@ public class HttpConnection {
* @param isSecure true if this connection is using HTTPS
* @param sslCertificates list of certificates presented by the client.
*/
public
HttpConnection
(
long
requestId
,
boolean
isSecure
,
X509Certificate
[]
sslCertificates
)
{
public
HttpConnection
(
long
requestId
,
boolean
isSecure
,
X509Certificate
[]
sslCertificates
,
AsyncContext
context
)
{
this
.
requestId
=
requestId
;
this
.
isSecure
=
isSecure
;
this
.
sslCertificates
=
sslCertificates
;
this
.
context
=
context
;
}
/**
* The connection should be closed without delivering a stanza to the requestor.
*/
public
void
close
()
{
if
(
isClosed
)
{
return
;
synchronized
(
this
)
{
if
(
isClosed
)
{
return
;
}
}
try
{
deliverBody
(
CONNECTION_CLOSED
);
deliverBody
(
null
);
}
catch
(
HttpConnectionClosedException
e
)
{
Log
.
warn
(
"Unexpected exception occurred while trying to close an HttpException."
,
e
);
}
catch
(
IOException
e
)
{
Log
.
warn
(
"Unexpected exception occurred while trying to close an HttpException."
,
e
);
}
}
...
...
@@ -85,7 +87,7 @@ public class HttpConnection {
*
* @return true if this connection has been closed.
*/
public
boolean
isClosed
()
{
public
synchronized
boolean
isClosed
()
{
return
isClosed
;
}
...
...
@@ -108,55 +110,20 @@ public class HttpConnection {
* @throws HttpConnectionClosedException when this connection to the client has already received
* a deliverable to forward to the client
*/
public
void
deliverBody
(
String
body
)
throws
HttpConnectionClosedException
{
public
void
deliverBody
(
String
body
)
throws
HttpConnectionClosedException
,
IOException
{
// We only want to use this function once so we will close it when the body is delivered.
synchronized
(
this
)
{
if
(
isClosed
)
{
throw
new
HttpConnectionClosedException
(
"The http connection is no longer "
+
"available to deliver content"
);
}
else
{
isClosed
=
true
;
}
}
if
(
body
==
null
)
{
body
=
CONNECTION_CLOSED
;
}
if
(
isSuspended
())
{
continuation
.
setAttribute
(
RESPONSE_BODY
,
body
);
continuation
.
resume
();
session
.
incrementServerPacketCount
();
}
else
{
this
.
body
=
body
;
isClosed
=
true
;
}
}
/**
* A call that will suspend the request if there is no deliverable currently available.
* Once the response becomes available, it is returned.
*
* @return the deliverable to send to the client
* @throws HttpBindTimeoutException to indicate that the maximum wait time requested by the
* client has been surpassed and an empty response should be returned.
*/
public
String
getResponse
()
throws
HttpBindTimeoutException
{
if
(
body
==
null
&&
continuation
!=
null
)
{
try
{
body
=
waitForResponse
();
}
catch
(
HttpBindTimeoutException
e
)
{
this
.
isClosed
=
true
;
throw
e
;
}
}
else
if
(
body
==
null
)
{
throw
new
IllegalStateException
(
"Continuation not set, cannot wait for deliverable."
);
}
else
if
(
CONNECTION_CLOSED
.
equals
(
body
))
{
return
null
;
if
(
body
==
null
)
{
body
=
HttpBindServlet
.
createEmptyBody
(
false
);
}
return
body
;
HttpBindServlet
.
respond
(
this
.
getSession
(),
this
.
context
,
body
)
;
}
/**
...
...
@@ -195,41 +162,6 @@ public class HttpConnection {
return
sslCertificates
;
}
void
setContinuation
(
Continuation
continuation
)
{
this
.
continuation
=
continuation
;
}
public
boolean
isSuspended
()
{
return
continuation
!=
null
&&
continuation
.
isSuspended
();
}
public
boolean
isExpired
()
{
return
continuation
!=
null
&&
continuation
.
isExpired
();
}
private
String
waitForResponse
()
throws
HttpBindTimeoutException
{
// we enter this method when we have no messages pending delivery
// when we resume a suspended continuation, or when we time out
if
(
continuation
.
isInitial
())
{
continuation
.
setTimeout
(
session
.
getWait
()
*
JiveConstants
.
SECOND
);
continuation
.
suspend
();
continuation
.
undispatch
();
}
else
if
(
continuation
.
isResumed
())
{
// This will occur when the hold attribute of a session has been exceeded.
String
deliverable
=
(
String
)
continuation
.
getAttribute
(
RESPONSE_BODY
);
if
(
deliverable
==
null
)
{
throw
new
HttpBindTimeoutException
();
}
else
if
(
CONNECTION_CLOSED
.
equals
(
deliverable
))
{
return
null
;
}
return
deliverable
;
}
throw
new
HttpBindTimeoutException
(
"Request "
+
requestId
+
" exceeded response time from "
+
"server of "
+
session
.
getWait
()
+
" seconds."
);
}
@Override
public
String
toString
()
{
return
(
session
!=
null
?
session
.
toString
()
:
"[Anonymous]"
)
...
...
src/java/org/jivesoftware/openfire/http/HttpSession.java
View file @
74ed3b77
This diff is collapsed.
Click to expand it.
src/java/org/jivesoftware/openfire/http/HttpSessionManager.java
View file @
74ed3b77
...
...
@@ -19,8 +19,8 @@
package
org
.
jivesoftware
.
openfire
.
http
;
import
java.io.IOException
;
import
java.net.InetAddress
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.TimerTask
;
import
java.util.concurrent.ConcurrentHashMap
;
...
...
@@ -33,19 +33,17 @@ import java.util.concurrent.atomic.AtomicInteger;
import
org.dom4j.DocumentException
;
import
org.dom4j.DocumentHelper
;
import
org.dom4j.Element
;
import
org.dom4j.QName
;
import
org.jivesoftware.openfire.SessionManager
;
import
org.jivesoftware.openfire.StreamID
;
import
org.jivesoftware.openfire.auth.UnauthorizedException
;
import
org.jivesoftware.util.JiveConstants
;
import
org.jivesoftware.util.JiveGlobals
;
import
org.jivesoftware.util.Log
;
import
org.jivesoftware.util.TaskEngine
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
/**
* Manages sessions for all users connecting to Openfire using the HTTP binding protoc
a
l,
* Manages sessions for all users connecting to Openfire using the HTTP binding protoc
o
l,
* <a href="http://www.xmpp.org/extensions/xep-0124.html">XEP-0124</a>.
*/
public
class
HttpSessionManager
{
...
...
@@ -78,7 +76,11 @@ public class HttpSessionManager {
JiveGlobals
.
migrateProperty
(
"xmpp.httpbind.worker.timeout"
);
this
.
sessionManager
=
SessionManager
.
getInstance
();
init
();
}
public
void
init
()
{
Log
.
warn
(
"HttpSessionManager.init() recreate sendPacketPool"
);
// Configure a pooled executor to handle async routing for incoming packets
// with a default size of 16 threads ("xmpp.httpbind.worker.threads"); also
// uses an unbounded task queue and configurable keep-alive (default: 60 secs)
...
...
@@ -144,7 +146,7 @@ public class HttpSessionManager {
/**
* Creates an HTTP binding session which will allow a user to exchange packets with Openfire.
*
* @param address the internet address that was used to bind to
Wildfi
e.
* @param address the internet address that was used to bind to
Openfir
e.
* @param rootNode the body element that was sent containing the request for a new session.
* @param connection the HTTP connection object which abstracts the individual connections to
* Openfire over the HTTP binding protocol. The initial session creation response is returned to
...
...
@@ -202,12 +204,15 @@ public class HttpSessionManager {
connection
.
deliverBody
(
createSessionCreationResponse
(
session
));
}
catch
(
HttpConnectionClosedException
e
)
{
/* This won't happen here. */
Log
.
error
(
"Error creating session."
,
e
);
throw
new
HttpBindException
(
"Internal server error"
,
BoshBindingError
.
internalServerError
);
}
catch
(
DocumentException
e
)
{
Log
.
error
(
"Error creating document"
,
e
);
throw
new
HttpBindException
(
"Internal server error"
,
BoshBindingError
.
internalServerError
);
Log
.
error
(
"Error creating session."
,
e
);
throw
new
HttpBindException
(
"Internal server error"
,
BoshBindingError
.
internalServerError
);
}
catch
(
IOException
e
)
{
Log
.
error
(
"Error creating session."
,
e
);
throw
new
HttpBindException
(
"Internal server error"
,
BoshBindingError
.
internalServerError
);
}
return
session
;
}
...
...
@@ -293,44 +298,6 @@ public class HttpSessionManager {
return
JiveGlobals
.
getIntProperty
(
"xmpp.httpbind.client.idle.polling"
,
60
);
}
/**
* Forwards a client request, which is related to a session, to the server. A connection is
* created and queued up in the provided session. When a connection reaches the top of a queue
* any pending packets bound for the client will be forwarded to the client through the
* connection.
*
* @param rid the unique, sequential, requestID sent from the client.
* @param session the HTTP session of the client that made the request.
* @param isSecure true if the request was made over a secure channel, HTTPS, and false if it
* was not.
* @param rootNode the XML body of the request.
* @return the created HTTP connection.
*
* @throws HttpBindException for several reasons: if the encoding inside of an auth packet is
* not recognized by the server, or if the packet type is not recognized.
* @throws HttpConnectionClosedException if the session is no longer available.
*/
public
HttpConnection
forwardRequest
(
long
rid
,
HttpSession
session
,
boolean
isSecure
,
Element
rootNode
)
throws
HttpBindException
,
HttpConnectionClosedException
{
//noinspection unchecked
List
<
Element
>
elements
=
rootNode
.
elements
();
boolean
isPoll
=
(
elements
.
size
()
==
0
);
if
(
"terminate"
.
equals
(
rootNode
.
attributeValue
(
"type"
)))
isPoll
=
false
;
else
if
(
"true"
.
equals
(
rootNode
.
attributeValue
(
new
QName
(
"restart"
,
rootNode
.
getNamespaceForPrefix
(
"xmpp"
)))))
isPoll
=
false
;
else
if
(
rootNode
.
attributeValue
(
"pause"
)
!=
null
)
isPoll
=
false
;
HttpConnection
connection
=
session
.
createConnection
(
rid
,
elements
,
isSecure
,
isPoll
);
if
(
elements
.
size
()
>
0
)
{
// creates the runnable to forward the packets
new
HttpPacketSender
(
session
).
init
();
}
return
connection
;
}
private
HttpSession
createSession
(
long
rid
,
InetAddress
address
,
HttpConnection
connection
)
throws
UnauthorizedException
{
// Create a ClientSession for this user.
StreamID
streamID
=
SessionManager
.
getInstance
().
nextStreamID
();
...
...
@@ -354,19 +321,7 @@ public class HttpSessionManager {
}
}
private
double
getDoubleAttribute
(
String
doubleValue
,
double
defaultValue
)
{
if
(
doubleValue
==
null
||
""
.
equals
(
doubleValue
.
trim
()))
{
return
defaultValue
;
}
try
{
return
Double
.
parseDouble
(
doubleValue
);
}
catch
(
Exception
ex
)
{
return
defaultValue
;
}
}
private
String
createSessionCreationResponse
(
HttpSession
session
)
throws
DocumentException
{
private
static
String
createSessionCreationResponse
(
HttpSession
session
)
throws
DocumentException
{
Element
response
=
DocumentHelper
.
createElement
(
"body"
);
response
.
addNamespace
(
""
,
"http://jabber.org/protocol/httpbind"
);
response
.
addNamespace
(
"stream"
,
"http://etherx.jabber.org/streams"
);
...
...
@@ -417,23 +372,7 @@ public class HttpSessionManager {
}
}
/**
* A runner that guarantees that the packets per a session will be sent and
* processed in the order in which they were received.
*/
private
class
HttpPacketSender
implements
Runnable
{
private
HttpSession
session
;
HttpPacketSender
(
HttpSession
session
)
{
this
.
session
=
session
;
}
public
void
run
()
{
session
.
sendPendingPackets
();
}
private
void
init
()
{
sendPacketPool
.
execute
(
this
);
}
protected
void
execute
(
Runnable
runnable
)
{
this
.
sendPacketPool
.
execute
(
runnable
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment