001: // CgiFrame.java
002: // $Id: CgiFrame.java,v 1.19 2003/04/30 20:08:23 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.jigsaw.frames;
007:
008: import java.io.File;
009: import java.io.IOException;
010: import java.io.InputStream;
011: import java.io.OutputStream;
012: import java.io.PrintStream;
013: import java.io.InputStreamReader;
014: import java.io.BufferedReader;
015:
016: import java.util.Enumeration;
017: import java.util.Hashtable;
018: import java.util.Vector;
019:
020: import java.net.InetAddress;
021: import java.net.MalformedURLException;
022: import java.net.URL;
023:
024: import org.w3c.tools.resources.Attribute;
025: import org.w3c.tools.resources.AttributeHolder;
026: import org.w3c.tools.resources.AttributeRegistry;
027: import org.w3c.tools.resources.BooleanAttribute;
028: import org.w3c.tools.resources.FileResource;
029: import org.w3c.tools.resources.FramedResource;
030: import org.w3c.tools.resources.InvalidParentException;
031: import org.w3c.tools.resources.LookupResult;
032: import org.w3c.tools.resources.LookupState;
033: import org.w3c.tools.resources.PropertiesAttribute;
034: import org.w3c.tools.resources.ProtocolException;
035: import org.w3c.tools.resources.Resource;
036: import org.w3c.tools.resources.ResourceException;
037: import org.w3c.tools.resources.ResourceFrame;
038: import org.w3c.tools.resources.ServerInterface;
039: import org.w3c.tools.resources.StringArrayAttribute;
040: import org.w3c.tools.resources.StringAttribute;
041:
042: import org.w3c.tools.resources.ProtocolException;
043:
044: import org.w3c.www.mime.MimeHeaderHolder;
045: import org.w3c.www.mime.MimeParser;
046: import org.w3c.www.mime.MimeParserException;
047: import org.w3c.www.mime.MimeParserFactory;
048: import org.w3c.www.mime.MimeType;
049:
050: import org.w3c.jigsaw.http.Client;
051: import org.w3c.jigsaw.http.HTTPException;
052: import org.w3c.jigsaw.http.Reply;
053: import org.w3c.jigsaw.http.Request;
054: import org.w3c.jigsaw.http.httpd;
055:
056: import org.w3c.www.http.HTTP;
057: import org.w3c.www.http.HeaderDescription;
058: import org.w3c.www.http.HttpEntityMessage;
059: import org.w3c.www.http.HttpEntityTag;
060: import org.w3c.www.http.HttpMessage;
061: import org.w3c.www.http.HttpReplyMessage;
062: import org.w3c.www.http.HttpRequestMessage;
063:
064: import org.w3c.jigsaw.auth.AuthFilter;
065: import org.w3c.util.ArrayDictionary;
066:
067: /**
068: * Parsing the CGI output - The CGIHeaderHolder, to hold CGI headers.
069: */
070: class CGIHeaderHolder implements MimeHeaderHolder {
071: // Status and Location deserve special treatments:
072: String status = null;
073: String location = null;
074: // Anyway, he is going to pay for using CGI
075: Hashtable headers = null;
076: // The MIME parse we are attached to:
077: MimeParser parser = null;
078:
079: /**
080: * The parsing is now about to start, take any appropriate action.
081: * This hook can return a <strong>true</strong> boolean value to enforce
082: * the MIME parser into transparent mode (eg the parser will <em>not</em>
083: * try to parse any headers.
084: * <p>This hack is primarily defined for HTTP/0.9 support, it might
085: * also be usefull for other hacks.
086: * @param parser The Mime parser.
087: * @return A boolean <strong>true</strong> if the MimeParser shouldn't
088: * continue the parsing, <strong>false</strong> otherwise.
089: * @exception IOException if an IO error occurs.
090: */
091:
092: public boolean notifyBeginParsing(MimeParser parser)
093: throws IOException {
094: return false;
095: }
096:
097: /**
098: * All the headers have been parsed, take any appropriate actions.
099: * @param parser The Mime parser.
100: * @exception IOException if an IO error occurs.
101: */
102:
103: public void notifyEndParsing(MimeParser parser) throws IOException {
104: return;
105: }
106:
107: /**
108: * A new header has been emited by the script.
109: * If the script is not an NPH, then it <strong>must</strong> at least
110: * emit one header, so we are safe here, although people may not be safe
111: * against the spec.
112: * @param name The header name.
113: * @param buf The header bytes.
114: * @param off The begining of the value bytes in above buffer.
115: * @param len The length of the value bytes in above buffer.
116: * @exception MimeParserException if parsing failed.
117: */
118:
119: public void notifyHeader(String name, byte buf[], int off, int len)
120: throws MimeParserException {
121: if (name.equalsIgnoreCase("status")) {
122: status = new String(buf, 0, off, len);
123: } else if (name.equalsIgnoreCase("location")) {
124: location = new String(buf, 0, off, len);
125: } else {
126: String extraval = new String(buf, 0, off, len);
127: if (headers == null) {
128: headers = new Hashtable(11);
129: } else {
130: String val = (String) headers.get(name.toLowerCase());
131: if (val != null)
132: extraval = val + "," + extraval;
133: }
134: headers.put(name.toLowerCase(), extraval);
135: }
136: }
137:
138: /**
139: * Get the status emited by the script.
140: */
141:
142: public String getStatus() {
143: return status;
144: }
145:
146: /**
147: * Get the location header value emited by the script.
148: */
149:
150: public String getLocation() {
151: return location;
152: }
153:
154: /**
155: * Get any header value (except status and location).
156: * @param name The name of the header to fetch.
157: * @return The string value of requested header, or <strong>null</strong>
158: * if header was not defined.
159: */
160:
161: public String getValue(String name) {
162: return (headers == null) ? null : (String) headers.get(name);
163: }
164:
165: /**
166: * Enumerate the headers defined by the holder.
167: * @return A enumeration of header names, or <strong>null</strong> if no
168: * header is defined.
169: */
170:
171: public Enumeration enumerateHeaders() {
172: if (headers == null)
173: return null;
174: return headers.keys();
175: }
176:
177: /**
178: * Get the remaining output of the stream.
179: * This should be called only once header parsing is done.
180: */
181:
182: public InputStream getInputStream() {
183: return parser.getInputStream();
184: }
185:
186: CGIHeaderHolder(MimeParser parser) {
187: this .parser = parser;
188: }
189:
190: }
191:
192: /**
193: * Parsing the CGI output - Always create a CGIHeaderHolder.
194: */
195:
196: class CGIHeaderHolderFactory implements MimeParserFactory {
197:
198: /**
199: * Create a new header holder to hold the parser's result.
200: * @param parser The parser that has something to parse.
201: * @return A MimeParserHandler compliant object.
202: */
203:
204: public MimeHeaderHolder createHeaderHolder(MimeParser parser) {
205: return new CGIHeaderHolder(parser);
206: }
207:
208: CGIHeaderHolderFactory() {
209: }
210:
211: }
212:
213: /**
214: * A simple process feeder class.
215: */
216:
217: class ProcessFeeder extends Thread {
218: Process proc = null;
219: OutputStream out = null;
220: InputStream in = null;
221: int count = -1;
222:
223: public void run() {
224: try {
225: byte buffer[] = new byte[4096];
226: int got = -1;
227:
228: // Send the data to the target process:
229: if (count >= 0) {
230: while ((count > 0) && ((got = in.read(buffer)) > 0)) {
231: out.write(buffer, 0, got);
232: count -= got;
233: }
234: } else {
235: while ((got = in.read(buffer)) > 0) {
236: out.write(buffer, 0, got);
237: }
238: }
239: } catch (Exception e) {
240: System.out.println("ProcessFeeder: caught exception !");
241: e.printStackTrace();
242: } finally {
243: // Clean up the process:
244: try {
245: out.flush();
246: } catch (IOException ex) {
247: }
248: try {
249: out.close();
250: } catch (IOException ex) {
251: }
252: try {
253: proc.waitFor();
254: } catch (Exception ex) {
255: }
256: }
257: }
258:
259: ProcessFeeder(Process proc, InputStream in) {
260: this (proc, in, -1);
261: }
262:
263: ProcessFeeder(Process proc, InputStream in, int count) {
264: this .proc = proc;
265: this .out = proc.getOutputStream();
266: this .in = in;
267: this .count = count;
268: }
269: }
270:
271: /**
272: * Handle CGI error stream
273: */
274: class ProcessErrorReader extends Thread {
275: BufferedReader r = null;
276:
277: public void run() {
278: String errline = null;
279: try {
280: errline = r.readLine();
281: if (errline != null) {
282: System.err.println("*** CgiError: " + errline);
283: }
284: } catch (Exception ex) {
285: } finally {
286: try {
287: r.close();
288: } catch (IOException ioex) {
289: }
290: }
291: }
292:
293: ProcessErrorReader(Process proc) {
294: r = new BufferedReader(new InputStreamReader(proc
295: .getErrorStream()));
296: }
297: }
298:
299: /**
300: * Handle CGI scripts.
301: */
302: public class CgiFrame extends HTTPFrame {
303:
304: private final static String STATE_EXTRA_PATH = "org.w3c.jigsaw.frames.CgiFrame.extraPath";
305:
306: /**
307: * Attribute index - The interpreter to use, if any.
308: */
309: protected static int ATTR_INTERPRETER = -1;
310: /**
311: * Attribute index - The array of string that makes the command to run.
312: */
313: protected static int ATTR_COMMAND = -1;
314: /**
315: * Attribute index - Does the script takes care of its headers ?
316: */
317: protected static int ATTR_NOHEADER = -1;
318: /**
319: * Attribute index - Does the script generates the form on GET ?
320: */
321: protected static int ATTR_GENERATES_FORM = -1;
322: /**
323: * Attribute index - Do DNS, to fill in REMOTE_HOST env var.
324: */
325: protected static int ATTR_REMOTE_HOST = -1;
326: /**
327: * Attribute index - Turn the script in debug mode.
328: */
329: protected static int ATTR_CGI_DEBUG = -1;
330: /**
331: * Attribute index - Additional environment vars
332: */
333: protected static int ATTR_ENV = -1;
334:
335: static {
336: Attribute a = null;
337: Class cls = null;
338: try {
339: cls = Class.forName("org.w3c.jigsaw.frames.CgiFrame");
340: } catch (Exception ex) {
341: ex.printStackTrace();
342: System.exit(1);
343: }
344: // The interpreter attribute:
345: a = new StringAttribute("interpreter", null, Attribute.EDITABLE);
346: ATTR_INTERPRETER = AttributeRegistry.registerAttribute(cls, a);
347: // The command attribute:
348: a = new StringArrayAttribute("command", null,
349: Attribute.MANDATORY | Attribute.EDITABLE);
350: ATTR_COMMAND = AttributeRegistry.registerAttribute(cls, a);
351: // The noheader attribute:
352: a = new BooleanAttribute("noheader", Boolean.FALSE,
353: Attribute.EDITABLE);
354: ATTR_NOHEADER = AttributeRegistry.registerAttribute(cls, a);
355: // The generates form attribute
356: a = new BooleanAttribute("generates-form", Boolean.TRUE,
357: Attribute.EDITABLE);
358: ATTR_GENERATES_FORM = AttributeRegistry.registerAttribute(cls,
359: a);
360: // Registerr the DODNS attribute.
361: a = new BooleanAttribute("remote-host", null,
362: Attribute.EDITABLE);
363: ATTR_REMOTE_HOST = AttributeRegistry.registerAttribute(cls, a);
364: // Register the debug mode flag:
365: a = new BooleanAttribute("cgi-debug", Boolean.FALSE,
366: Attribute.EDITABLE);
367: ATTR_CGI_DEBUG = AttributeRegistry.registerAttribute(cls, a);
368: // The env attribute:
369: a = new PropertiesAttribute("environment", null,
370: Attribute.EDITABLE);
371: ATTR_ENV = AttributeRegistry.registerAttribute(cls, a);
372: }
373:
374: /**
375: * Get the interpreter to use to execute the script.
376: * This is most usefull for operating systems that don't have a
377: * <code>!#</code> convention ala UNIX.
378: * @return The interpreter to run the script.
379: */
380:
381: public String getInterpreter() {
382: return getString(ATTR_INTERPRETER, null);
383: }
384:
385: /**
386: * Get the command string array.
387: */
388:
389: public String[] getCommand() {
390: return (String[]) getValue(ATTR_COMMAND, null);
391: }
392:
393: public ArrayDictionary getUserEnv() {
394: return (ArrayDictionary) getValue(ATTR_ENV, null);
395: }
396:
397: /**
398: * Get the noheader flag.
399: * @return The boolean value of the noheader flag.
400: */
401:
402: public boolean checkNoheaderFlag() {
403: return getBoolean(ATTR_NOHEADER, false);
404: }
405:
406: /**
407: * Get the generates form flag.
408: * @return The boolean value of the generates form flag.
409: */
410:
411: public boolean checkGeneratesFormFlag() {
412: return getBoolean(ATTR_GENERATES_FORM, true);
413: }
414:
415: /**
416: * Get the remote host attribute value.
417: * If turned on, this flag will enable the REMOTE_HOST env var computation.
418: * @return A boolean.
419: */
420:
421: public boolean checkRemoteHost() {
422: return getBoolean(ATTR_REMOTE_HOST, false);
423: }
424:
425: /**
426: * Get the CGI debug flag.
427: * @return The boolean value of the CGI debug flag.
428: */
429:
430: public boolean checkCgiDebug() {
431: return getBoolean(ATTR_CGI_DEBUG, false);
432: }
433:
434: /**
435: * Turn the given header name into it's env var canonical name.
436: * This guy is crazy enough to run CGI scripts, he can pay for that
437: * overhead.
438: * @param name The header name.
439: * @return A String giving the official env variable name for that header.
440: */
441:
442: public String getEnvName(String name) {
443: int sl = name.length();
444: StringBuffer sb = new StringBuffer(5 + sl);
445: sb.append("HTTP_");
446: for (int i = 0; i < sl; i++) {
447: char ch = name.charAt(i);
448: sb.append((ch == '-') ? '_' : Character.toUpperCase(ch));
449: }
450: return sb.toString();
451: }
452:
453: /**
454: * Get this resource Etag
455: * @return an instance of HttpEntityTag, or <strong>null</strong> if not
456: * defined.
457: */
458: public HttpEntityTag getETag() {
459: if (etag == null) {
460: HttpEntityTag netag = super .getETag();
461: if (netag != null) {
462: etag.setWeak(true);
463: }
464: }
465: return etag;
466: }
467:
468: /**
469: * Handle the CGI script output.
470: * This methods handles the CGI script output. Depending on the
471: * value of the <strong>noheader</strong> attribute it either:
472: * <ul>
473: * <li>Sends back the script output directly,</li>
474: * <li>Parses the script output, looking for a status header or a
475: * location header, or a content-length header, or any combination
476: * of those three.
477: * </ul>
478: * @param process The underlying CGI process.
479: * @param request The processed request.
480: * @exception ProtocolException If an HTTP error
481: * should be sent back to the client.
482: */
483:
484: protected Reply handleCGIOutput(Process process, Request request)
485: throws ProtocolException {
486: // first, take care of error stream
487: (new ProcessErrorReader(process)).start();
488: // No header script don't deserve attention:
489: if (checkNoheaderFlag()) {
490: Reply reply = request.makeReply(HTTP.NOHEADER);
491: reply.setStream(process.getInputStream());
492: return reply;
493: }
494: // Check for debugging mode:
495: if (checkCgiDebug()) {
496: Reply reply = request.makeReply(HTTP.OK);
497: reply.setContentType(MimeType.TEXT_PLAIN);
498: reply.setStream(process.getInputStream());
499: return reply;
500: }
501: // We MUST parse at least one header:
502: MimeParser p = new MimeParser(process.getInputStream(),
503: new CGIHeaderHolderFactory());
504: Reply reply = null;
505: try {
506: CGIHeaderHolder h = (CGIHeaderHolder) p.parse();
507: // Check for a status code:
508: String svalue = h.getStatus();
509: String location = h.getLocation();
510: if (svalue != null) {
511: int status = -1;
512: try {
513: String _st = svalue.trim();
514: int _space = _st.indexOf(' ');
515: if (_space != -1) {
516: _st = _st.substring(0, _space);
517: }
518: status = Integer.parseInt(_st);
519: } catch (Exception ex) {
520: // This script has emited an invalid status line:
521: String msg = ("Emited an invalid status line ["
522: + svalue + "].");
523: getServer().errlog(this , msg);
524: // Throw an HTTPException:
525: reply = request
526: .makeReply(HTTP.INTERNAL_SERVER_ERROR);
527: reply
528: .setContent("CGI script emited invalid status.");
529: throw new HTTPException(reply);
530: }
531: // we will use the default for this frame, but remove
532: // the length calculated from the script size.
533: reply = createDefaultReply(request, status);
534: reply.setContentLength(-1);
535: } else {
536: // No status code available, any location header ?
537: if (location != null) {
538: reply = request.makeReply(HTTP.FOUND);
539: } else {
540: reply = createDefaultReply(request, HTTP.OK);
541: reply.setContentLength(-1);
542: }
543: }
544: // Set up the location header if needed:
545: if (location != null) {
546: try {
547: reply
548: .setLocation(new URL(getURL(request),
549: location));
550: } catch (MalformedURLException ex) {
551: // This should really not happen:
552: getServer().errlog(
553: this ,
554: "unable to create location url " + location
555: + " in base " + getURL(request));
556: }
557: }
558: // And then, the remaining headers:
559: Enumeration e = h.enumerateHeaders();
560: if (e != null) {
561: while (e.hasMoreElements()) {
562: String hname = (String) e.nextElement();
563: reply.setValue(hname, (String) h.getValue(hname));
564: }
565: }
566: reply.setStream(p.getInputStream());
567: } catch (IOException ex) {
568: ex.printStackTrace();
569: } catch (MimeParserException ex) {
570: // This script has generated invalid output:
571: String msg = (getURL(request) + ": emited invalid output ["
572: + ex.getMessage() + "]");
573: getServer().errlog(this , msg);
574: // Throw an HTTPException:
575: Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR);
576: error
577: .setContent("CGI error: unable to parse script headers.");
578: throw new HTTPException(error);
579: }
580: if (reply != null) {
581: reply.setDynamic(true);
582: }
583: return reply;
584: }
585:
586: /**
587: * Add an enviornment binding to the given vector.
588: * @param name The name of the enviornment variable.
589: * @param val Its value.
590: * @param into The vector to which accumulate bindings.
591: */
592:
593: private void addEnv(String name, String val, Vector into) {
594: into.addElement(name + "=" + val);
595: }
596:
597: /**
598: * Prepare the command to run for this CGI script, and run it.
599: * @param request The request to handle.
600: * @return The running CGI process object.
601: * @exception ProtocolException If we weren't able
602: * to build the command or the environment.
603: * @exception IOException if an IO erro occurs.
604: */
605:
606: protected Process makeCgiCommand(Request request)
607: throws ProtocolException, IOException {
608: // Check the command attribute first:
609: String query = null;
610: String command[] = getCommand();
611: if (command == null) {
612: Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR);
613: error
614: .setContent("CgiResource mis-configured: it doesn't have a "
615: + " command attribute");
616: throw new HTTPException(error);
617: }
618: // Ok:
619: Vector env = new Vector(32);
620: httpd server = request.getClient().getServer();
621: InetAddress sadr = server.getInetAddress();
622: // Specified environment variables:
623: // We do not handle the following variables:
624: // - REMOTE_IDENT: would require usage of IDENT protocol.
625: // Authentification type, if any:
626: String svalue = (String) request
627: .getState(AuthFilter.STATE_AUTHTYPE);
628: if (svalue != null)
629: addEnv("AUTH_TYPE", svalue, env);
630: // Content length, if available:
631: svalue = request.getValue("content-length");
632: if (svalue != null)
633: addEnv("CONTENT_LENGTH", svalue, env);
634: // Content type, if available:
635: svalue = request.getValue("content-type");
636: if (svalue != null)
637: addEnv("CONTENT_TYPE", svalue, env);
638: // The gateway interface, hopefully 1.1 !
639: addEnv("GATEWAY_INTERFACE", "CGI/1.1", env);
640: // The PATH_INFO, which I am afraid I still don't understand:
641: svalue = (String) request.getState(STATE_EXTRA_PATH);
642: if (svalue == null)
643: addEnv("PATH_INFO", "/", env);
644: else
645: addEnv("PATH_INFO", svalue, env);
646: // The query string:
647: query = request.getQueryString();
648: if (query != null)
649: addEnv("QUERY_STRING", query, env);
650: // The remote client IP address:
651: svalue = request.getClient().getInetAddress().toString();
652: addEnv("REMOTE_ADDR", svalue, env);
653: // Authentified user:
654: svalue = (String) request.getState(AuthFilter.STATE_AUTHUSER);
655: if (svalue != null)
656: addEnv("REMOTE_USER", svalue, env);
657: // Remote host name, if allowed:
658: if (checkRemoteHost()) {
659: String host = request.getClient().getInetAddress()
660: .getHostName();
661: addEnv("REMOTE_HOST", host, env);
662: }
663: // The request method:
664: addEnv("REQUEST_METHOD", request.getMethod(), env);
665: // The script name :
666: addEnv("SCRIPT_NAME", getURLPath(), env);
667: // Server name:
668: addEnv("SERVER_NAME", getServer().getHost(), env);
669: // Server port:
670: svalue = Integer.toString(getServer().getLocalPort());
671: addEnv("SERVER_PORT", svalue, env);
672: // Server protocol:
673: addEnv("SERVER_PROTOCOL", request.getVersion(), env);
674: // Server software:
675: addEnv("SERVER_SOFTWARE", server.getSoftware(), env);
676: if (getFileResource() != null) {
677: addEnv("PATH_TRANSLATED", getFileResource().getFile()
678: .getAbsolutePath(), env);
679: }
680:
681: //Add user env
682: ArrayDictionary uenv = getUserEnv();
683: if (uenv != null) {
684: Enumeration names = uenv.keys();
685: while (names.hasMoreElements()) {
686: String var = (String) names.nextElement();
687: addEnv(var, (String) uenv.get(var), env);
688: }
689: }
690: // All other request fields, yeah, let's lose even more time:
691: Enumeration e = request.enumerateHeaderDescriptions(false);
692: while (e.hasMoreElements()) {
693: HeaderDescription d = (HeaderDescription) e.nextElement();
694: addEnv(getEnvName(d.getName()), request.getHeaderValue(d)
695: .toString(), env);
696: }
697: // Command line:
698: if (query != null) {
699: String querycmd[] = new String[command.length + 1];
700: System.arraycopy(command, 0, querycmd, 0, command.length);
701: querycmd[command.length] = query;
702: command = querycmd;
703: }
704: String aenv[] = new String[env.size()];
705: env.copyInto(aenv);
706: // Run the process:
707: if (getInterpreter() != null) {
708: String run[] = new String[command.length + 1];
709: run[0] = getInterpreter();
710: System.arraycopy(command, 0, run, 1, command.length);
711: return Runtime.getRuntime().exec(run, aenv);
712: } else {
713: return Runtime.getRuntime().exec(command, aenv);
714: }
715: }
716:
717: /**
718: * Lookup sub-resources.
719: * Accumulate the remaning path in some special state of the request.
720: * <p>This allows us to implement the <code>PATH_INFO</code>
721: * CGI variable properly.
722: * @param ls Current lookup state.
723: * @param lr Lookup result under construction.
724: * @return A boolean <strong>true</strong> if lookup should continue,
725: * <strong>false</strong> otherwise.
726: * @exception ProtocolException (fixme doc)
727: */
728:
729: public boolean lookup(LookupState ls, LookupResult lr)
730: throws ProtocolException {
731: // Get the extra path information:
732: String extraPath = ls.getRemainingPath(true);
733: if ((extraPath == null) || extraPath.equals(""))
734: extraPath = "/";
735: // Keep this path info into the request, if possible:
736: Request request = (Request) ls.getRequest();
737: if (request != null)
738: request.setState(STATE_EXTRA_PATH, extraPath);
739: lr.setTarget(getResource().getResourceReference());
740: return super .lookup(ls, lr);
741: }
742:
743: /**
744: * GET method implementation.
745: * this method is splitted into two cases:
746: * <p>If the resource is able to generates its form, than run the script
747: * to emit the form. Otherwsie, use our super class (FileResource) ability
748: * to send the file that contains the form.
749: * <p>Note that there is no need to feed the underlying process with
750: * data in the GET case.
751: * @param request The request to handle.
752: * @exception ProtocolException If processing the request failed.
753: * @exception ResourceException If the resource got a fatal error.
754: */
755: public Reply get(Request request) throws ProtocolException,
756: ResourceException {
757: if (!checkGeneratesFormFlag())
758: return super .get(request);
759: Process process = null;
760: try {
761: process = makeCgiCommand(request);
762: } catch (IOException e) {
763: e.printStackTrace();
764: Reply error = request.makeReply(HTTP.NOT_FOUND);
765: error.setContent("The resource's script wasn't found.");
766: throw new HTTPException(error);
767: }
768: return handleCGIOutput(process, request);
769: }
770:
771: /**
772: * Handle the POST method according to CGI/1.1 specification.
773: * The request body is sent back to the launched CGI script, as is, and
774: * the script output is handled by the handleCGIOutput method.
775: * @param request The request to process.
776: * @exception ProtocolException If the processing failed.
777: * @exception ResourceException If the resource got a fatal error.
778: */
779: public Reply post(Request request) throws ProtocolException,
780: ResourceException {
781: Process process = null;
782: // Launch the CGI process:
783: try {
784: process = makeCgiCommand(request);
785: } catch (IOException ex) {
786: // The process wasn't executable, emit a errlog message:
787: String msg = ("The process " + getCommand()[0]
788: + " couldn't be executed [" + ex.getMessage() + "]");
789: getServer().errlog(this , msg);
790: // Throw an internal server error:
791: Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR);
792: error.setContent("CGI script is misconfigured.");
793: throw new HTTPException(error);
794: }
795: // Now feed the process:
796: try {
797: // Send the 100 status code:
798: Client client = request.getClient();
799: if (client != null)
800: client.sendContinue();
801: InputStream in = request.getInputStream();
802: if (in == null) {
803: // There was no input to that CCI, close process stream
804: process.getOutputStream().close();
805: } else {
806: // Some input to feed the process with:
807: (new ProcessFeeder(process, in)).start();
808: }
809: } catch (IOException ex) {
810: // This is most probably a bad request:
811: Reply error = request.makeReply(HTTP.BAD_REQUEST);
812: error.setContent("The request didn't have a valid input.");
813: throw new HTTPException(error);
814: }
815: return handleCGIOutput(process, request);
816: }
817:
818: /**
819: * At register time, if no command, use a suitable default.
820: * THis method will set the command to the identifier, if it is not
821: * provided.
822: * @param values Default attribute values.
823: */
824:
825: public void registerResource(FramedResource resource) {
826: super .registerResource(resource);
827: //if no command is specified look for a file resource attached
828: //and get its File absolute path if available.
829: if (getCommand() == null) {
830: if (getFileResource() != null) {
831: try {
832: if (getFileResource().getFile() != null) {
833: String cmd[] = new String[1];
834: cmd[0] = getFileResource().getFile()
835: .getAbsolutePath();
836: setValue(ATTR_COMMAND, cmd);
837: }
838: } catch (InvalidParentException ex) {
839: //nothing to do
840: //probably an indexer operation
841: }
842: }
843: }
844: }
845: }
|