G-WAN,从CGI脚本输出头部信息

huangapple go评论116阅读模式
英文:

G-WAN, output headers from CGI script

问题

我正在尝试在CGI脚本中设置一个类似于Content-Type的HTTP头。

PHP中:

header('Content-Type: text/plain');
// 或者
echo 'Content-Type: text/plain', "\r\n\r\n"; // 作为第一行

或者在Go中:

fmt.Print("Content-Type: text/plain\r\n\r\n") // 作为第一行

这两种方法对输出没有影响。

如何实现这个功能?

编辑

我还尝试了在Go中使用CGI包的以下方法:

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)
}

但是我得到了以下错误:

cgi: failed to parse REQUEST_URI into a URL:
英文:

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

得分: 3

问题1:

如果您的脚本返回一个有效的HTTP返回代码(如200),那么G-WAN会构建相应的HTTP头,除非它们已经存在(在这里以"HTTP/1.x 200 OK"开头)。

因此,要在脚本语言中强制指定给定的content-type(除了支持G-WAN API的语言,如C、C++、D和Objective-C之外),您需要return 1并定义您回复的所有HTTP头。

支持G-WAN API的编程语言可以使用get_env(argv, REPLY_MIME_TYPE);(如fractal.c和其他示例所示),并让G-WAN构建其余的头部。

问题2:

环境变量REQUEST_URI(虽然有用)不是支持的CGI v1规范(RFC-3875)的一部分。我已经要求在将来的版本中添加REQUEST_URI

G-WAN提供的脚本示例列出了v3.12支持的变量:

// ----------------------------------------------------------------------------
// CGI/1.1环境变量:
// ----------------------------------------------------------------------------
// "AUTH_TYPE",          // "" | "Basic" | "Digest" | 等等
// "CONTENT_LENGTH",     // "" | 实体长度
// "CONTENT_TYPE",       // "" | 内容类型
// "GATEWAY_INTERFACE",  // "CGI/1.1"
// "PATH_INFO",          // "" | ( "/" 路径 )
// "PATH_TRANSLATED",    // PATH_INFO的磁盘文件名
// "QUERY_STRING",       // "" | ?"hellox.c&name=toto"
// "REMOTE_ADDR",        // 客户端IP地址
// "REMOTE_HOST",        // 客户端DNS名称(或IP地址)
// "REMOTE_IDENT",       // 客户端身份(RFC 1413),可选
// "REMOTE_USER",        // 客户端身份(如果有认证)
// "REQUEST_METHOD",     // "GET" | "HEAD" | "PUT"等等
// "SCRIPT_NAME",        // "" | ("/" 路径 "hello.c")
// "SERVER_NAME",        // "gwan.com" | IP地址
// "SERVER_PORT",        // "80"
// "SERVER_PROTOCOL",    // "HTTP/1.1" | "HTTP/1.0" | "HTTP/0.9"
// "SERVER_SOFTWARE",    // "G-WAN"
// ----------------------------------------------------------------------------

请注意,您可以使用以下(更快)的Go代码访问请求和参数(如果有):

// 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])
 }

更新

我们通过电子邮件收到了这段源代码:

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)  
}

请注意,此代码是不正确的:它甚至无法编译 - G-WAN报告以下错误:

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")


要运行G-WAN,您必须修复错误或删除此Servlet。

这很可能是您没有看到程序被“更新”的原因:如果旧版本存在,而在G-WAN运行时没有被错误版本替换。

当您开发(编辑脚本)时,应始终查看终端以检查您新编辑的代码是否编译。

我建议您查看(工作的)hello.go示例,以了解main()和(强制性的)return code的预期定义的要求。

当不使用返回代码时(如您的代码中),G-WAN将注入默认的HTTP头(在您的情况下是HTTP/0.9 200 OK),这将绕过您的HTTP头(如果有的话),结果是Internet浏览器将等待直到超时,因为它不知道您的回复的长度。

如示例和手册中所述,要告诉G-WAN不要创建HTTP头,您必须返回1-99范围内的值(0表示关闭连接200-600保留给HTTP返回代码,告诉G-WAN生成相应的HTTP头)。

英文:

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).

huangapple
  • 本文由 发表于 2013年1月16日 19:56:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/14357907.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定