- From: Anselm Baird_Smith <abaird@www43.inria.fr>
- Date: Wed, 6 Nov 1996 11:39:35 +0100 (MET)
- To: www-jigsaw@w3.org
For those of you who have installed the latest version of that file, I
recommend patching it again. I won't send a patch (most people have
asked for the full version last time), so I have enclosed it below.
The problem was when using Jigsaw as a proxy (if you are not doing so,
you don't need that new version, although I recommend using it, so
that it gets checked).
Modification is in getInputStream, whic takes care properly of broken
1.0 requests...
Anselm.
// Request.java
// $Id: Request.java,v 1.16 1996/10/22 08:34:57 abaird Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html
package w3c.jigsaw.http ;
import java.io.*;
import java.util.*;
import java.net.*;
import w3c.www.mime.* ;
import w3c.www.http.* ;
import w3c.util.*;
import w3c.jigsaw.resources.*;
/**
* this class extends HttpRequestMessage to cope with HTTP request.
* One subtely here: note how each field acessor <em>never</em> throws an
* exception, but rather is provided with a default value: this is in the hope
* that sometime, HTTP will not require all the parsing it requires right now.
*/
public class Request extends HttpRequestMessage implements HTTP {
/**
* The URL that means <strong>*</strong> for an <code>OPTIONS</code>
* method.
*/
public static URL THE_SERVER = null;
static {
try {
THE_SERVER = new URL("http://your.url.unknown");
} catch (Exception ex) {
ex.printStackTrace();
}
}
protected Client client = null;
protected MimeParser parser = null;
protected InputStream in = null;
protected boolean keepcon = true;
boolean is_proxy = false;
/**
* Fix the target URL of the request, this is the only good time to do so.
*/
public void notifyEndParsing(MimeParser parser)
throws HttpParserException, IOException
{
super.notifyEndParsing(parser);
String target = getTarget();
String url = null;
// Get rid of the nasty cases (who deserve a place in hell ?)
if ( target.equals("*") ) {
setURL(THE_SERVER);
return;
}
// Is this a full http URL:
int colon = target.indexOf(':');
if ((colon != -1) && target.substring(0, colon).equals("http")) {
// Good we have a full URL:
url = target;
} else {
// Do we have a valid host header ?
String host = getHost();
if ( host == null )
url = getClient().getServer().getURL() + target;
else
url = "http://" + host + target;
}
// Now parse the URL:
try {
setURL(new URL(url));
} catch (Exception ex) {
throw new HttpParserException("Invalid URL ["+url+"]");
}
// not reached !
}
// FIXME
// This guy should also check that the (optional) request stream has been
// exhausted.
public boolean canKeepConnection() {
// HTTP/0.9 doesn't know about keeping connections alive:
if (( ! keepcon) || (major < 1))
return false;
if ( minor >= 1 )
// HTTP/1.1 keeps connections alive by default
return hasConnection("close") ? false : true;
// For HTTP/1.0 check the [proxy] connection header:
if ( is_proxy )
return hasProxyConnection("keep-alive");
else
return hasConnection("keep-alive");
}
private HTTPResource target_resource = null;
protected void setTargetResource(HTTPResource resource) {
target_resource = resource;
}
/**
* Get this request target resource.
* @return An instance of HTTPResource, or <strong>null</strong> if
* not found.
*/
public HTTPResource getTargetResource() {
return target_resource;
}
public void setProxy(boolean onoff) {
is_proxy = onoff;
}
public boolean isProxy() {
return is_proxy;
}
public String getURLPath() {
return url.getFile();
}
public void setURLPath(String path) {
try {
url = new URL(url, path);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public boolean hasContentLength() {
return hasHeader(H_CONTENT_LENGTH);
}
public boolean hasContentType() {
return hasHeader(H_CONTENT_TYPE);
}
public boolean hasAccept() {
return hasHeader(H_ACCEPT);
}
public boolean hasAcceptEncoding() {
return hasHeader(H_ACCEPT_ENCODING);
}
public boolean hasAcceptLanguage() {
return hasHeader(H_ACCEPT_LANGUAGE);
}
public boolean hasAuthorization() {
return hasHeader(H_AUTHORIZATION);
}
public boolean hasProxyAuthorization() {
return hasHeader(H_PROXY_AUTHORIZATION);
}
public String getQueryString() {
return (String) getState("query");
}
public boolean hasQueryString() {
return hasState("query");
}
protected boolean internal = false;
public boolean isInternal() {
return internal;
}
public void setInternal(boolean onoff) {
this.internal = onoff;
}
protected Request original = null;
public Request getOriginal() {
return original == null ? this : original ;
}
/**
* Clone this request, in order to launch an internal request.
* This method can be used to run a request in some given context, defined
* by an original request. It will preserve all the original information
* (such as authentication, etc), and will provide a <em>clone</em> of
* the original request.
* <p>The original request and its clone differ in the following way:
* <ul>
* <li>The clone is marked as <em>internal</em>, which can be tested
* by the <code>isInternal</code> method.
* <li>The clone will keep a pointer to the first request that was
* cloned. This original request can be accessed by the <code>getOriginal
* </code> method.
* </ul>
* <p>To run an internal request, the caller can then use the <code>
* w3c.jigsaw.http.httpd</code> <code>perform</code> method.
* @return A fresh Request instance, marked as internal.
*/
public HttpMessage getClone() {
Request cl = (Request) super.getClone();
cl.internal = true;
if ( cl.original == null )
cl.original = this;
return cl;
}
/**
* Get this reply entity body.
* The reply entity body is returned as an InputStream, that the caller
* has to read to actually get the bytes of the content.
* @return An InputStream instance. If the reply has no body, the returned
* input stream will just return <strong>-1</strong> on first read.
*/
public InputStream getInputStream()
throws IOException
{
if ( in != null )
return in;
// Find out which method is used to the length:
int len = getContentLength();
if ( len >= 0 ) {
in = new ContentLengthInputStream(parser.getInputStream(), len);
} else {
// No content length, try out chunked encoding:
String te[] = getTransferEncoding() ;
if ( te != null ) {
for (int i = 0 ; i < te.length ; i++) {
if (te[i].equals("chunked"))
in = new ChunkedInputStream(parser.getInputStream());
}
}
}
// Handle broken HTTP/1.0 request
// It is mandatory for 1.1 requests to have been handled above.
if ((major == 1) && (minor == 0) && (in == null)) {
String m = getMethod();
if (m.equals("POST") || m.equals("PUT")) {
keepcon = false;
in = parser.getInputStream();
}
}
return in;
}
/**
* Unescape a HTTP escaped string
* @param s The string to be unescaped
* @return the unescaped string.
*/
public static String unescape (String s) {
StringBuffer sbuf = new StringBuffer () ;
int l = s.length() ;
int ch = -1 ;
for (int i = 0 ; i < l ; i++) {
switch (ch = s.charAt(i)) {
case '%':
ch = s.charAt (++i) ;
int hb = (Character.isDigit ((char) ch)
? ch - '0'
: Character.toLowerCase ((char) ch) - 'a') & 0xF ;
ch = s.charAt (++i) ;
int lb = (Character.isDigit ((char) ch)
? ch - '0'
: Character.toLowerCase ((char) ch) - 'a') & 0xF ;
sbuf.append ((char) ((hb << 4) | lb)) ;
break ;
case '+':
sbuf.append (' ') ;
break ;
default:
sbuf.append ((char) ch) ;
}
}
return sbuf.toString() ;
}
/**
* Make an empty Reply object matching this request version.
* @param status The status of the reply.
*/
public Reply makeReply(int status) {
return new Reply(client, getMajorVersion(), getMinorVersion(), status);
}
/**
* Get the client of this request.
*/
public Client getClient() {
return client ;
}
public Request (Client client, MimeParser parser) {
super (parser);
this.parser = parser;
this.client = client ;
}
}
Received on Wednesday, 6 November 1996 05:39:40 UTC