G-WAN, output headers from CGI script

2019-06-26 06:41发布

问题:

I'm trying to set an HTTP header like Content-Type over a CGI script.

In PHP:

header('Content-Type: text/plain');
// or
echo 'Content-Type: text/plain', "\r\n\r\n"; // as first line

or in Go:

fmt.Print("Content-Type: text/plain\r\n\r\n") // as first line

Both have no effect on the output.

How can this be done?

EDIT

I also tried the following in Go, using the CGI package:

package main

import "fmt"
import "os"
import "net/http/cgi"

func main() {
    r,e := cgi.Request()
    if e != nil {
        fmt.Println(e)
        os.Exit(200)
    }
    fmt.Printf("%#v", r)
    os.Exit(200)
}

but I get the error:

cgi: failed to parse REQUEST_URI into a URL:

回答1:

Question 1:

If your script returns a valid HTTP return code (like 200) then G-WAN builds the corresponding HTTP Headers unless they are already there (starting with "HTTP/1.x 200 OK" here).

So, to force a given content-type with scripted languages (other than those which support the G-WAN API like C, C++, D, and Objective-C) you would have to return 1 and define ALL the HTTP headers of your reply.

The programming languages that support the G-WAN API can use get_env(argv, REPLY_MIME_TYPE); (as shown in fractal.c and others) and let G-WAN build the rest of the headers.

Question 2:

The environment variable REQUEST_URI (while useful) is not part of the supported CGI v1 specification (RFC-3875). I have requested that REQUEST_URI is added in a future release.

The script examples provided with G-WAN list the supported variables by v3.12:

// ----------------------------------------------------------------------------
// CGI/1.1 environment variables:
// ----------------------------------------------------------------------------
// "AUTH_TYPE",          // "" | "Basic" | "Digest" | etc.
// "CONTENT_LENGTH",     // "" | entity_length
// "CONTENT_TYPE",       // "" | content_type
// "GATEWAY_INTERFACE",  // "CGI/1.1"
// "PATH_INFO",          // "" | ( "/" path )
// "PATH_TRANSLATED",    // disk filename for PATH_INFO
// "QUERY_STRING",       // "" | ?"hellox.c&name=toto"
// "REMOTE_ADDR",        // client IP address
// "REMOTE_HOST",        // client DNS name (or IP addr)
// "REMOTE_IDENT",       // client identity (RFC 1413), opt
// "REMOTE_USER",        // client identity (if auth)
// "REQUEST_METHOD",     // "GET" | "HEAD" | "PUT", etc.
// "SCRIPT_NAME",        // "" | ("/" path "hello.c")
// "SERVER_NAME",        // "gwan.com" | IP address
// "SERVER_PORT",        // "80"
// "SERVER_PROTOCOL",    // "HTTP/1.1" | "HTTP/1.0" | "HTTP/0.9"
// "SERVER_SOFTWARE",    // "G-WAN"
// ----------------------------------------------------------------------------

Note that you can however access both the request and the parameters (if any) by using the following (and faster) Go code:

// args[1] /opt/gwan/10.10.20.80_80/#192.168.200.80/csp/hello.go
// args[2] arg1=123
// args[3] arg2=456

for i := 1; i < len(os.Args); i++ {
   fmt.Printf("args[%d] %s<br>", i, os.Args[i])
 }

UPDATE

We received this source code by email:

package main

import "fmt"
import "os"

func main() 
{
   p := "<h1>Hello world!</h1><p>This is dog bla</p>"
   fmt.Printf("%s 200 OK\r\n", os.Getenv("SERVER_PROTOCOL"))
   fmt.Print("Content-Type: text/html; charset=UTF-8\r\n")
   fmt.Print("Connection: Keep-Alive\r\n")
   fmt.Printf("Content-Length: %d\r\n",len(p))
   fmt.Print("\r\n")
   fmt.Print(p)  
}

Please note that this code is incorrect: it does not even compile - and G-WAN reports the following errors:

loading.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Error: hell.go
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# command-line-arguments
0.0.0.0_8080/#0.0.0.0/csp/hell.go:7: syntax error: unexpected semicolon or newline before {
0.0.0.0_8080/#0.0.0.0/csp/hell.go:9: non-declaration statement outside function body
0.0.0.0_8080/#0.0.0.0/csp/hell.go:10: non-declaration statement outside function body
0.0.0.0_8080/#0.0.0.0/csp/hell.go:11: non-declaration statement outside function body
0.0.0.0_8080/#0.0.0.0/csp/hell.go:12: non-declaration statement outside function body
0.0.0.0_8080/#0.0.0.0/csp/hell.go:13: non-declaration statement outside function body
0.0.0.0_8080/#0.0.0.0/csp/hell.go:14: non-declaration statement outside function body
0.0.0.0_8080/#0.0.0.0/csp/hell.go:16: syntax error: unexpected }

 4|import "os"
 5|
 6|func main() 
 7!{
 8|   p := "<h1>Hello world!</h1><p>This is dog bla</p>"
 9|   fmt.Printf("%s 200 OK\r\n", os.Getenv("SERVER_PROTOCOL"))
10|   fmt.Print("Content-Type: text/html; charset=UTF-8\r\n")
11|   fmt.Print("Connection: Keep-Alive\r\n")


To run G-WAN, you must fix the error(s) or remove this Servlet.

This is most probably why you haven't seen the program being "updated": the old version, if any, was not replaced by the faulty version updated while G-WAN was running.

When you develop (editing scripts) you should always have a look at the terminal to check if your newly edited code compiles.

I recommend you to look at the (working) hello.go example to see what the requirements are for the expected definition of main() and the (madatory) return code.

When no return code is used (like in your code), G-WAN will inject default HTTP headers (HTTP/0.9 200 OK in your case) which will bypass your HTTP headers (if any) and as a result the Internet Browser will wait until it times-out because it does not know the length of your reply.

As documented in the examples and in the manual, to tell G-WAN not to create HTTP headers you have to return a value in the 1-99 range (0 means close connection and 200-600 is reserved for HTTP return codes that tell G-WAN to generate the correspondig HTTP headers).



标签: php http cgi go g-wan