英文:
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).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论