请求报文:由请求行,头部字段集合,消息正文三大部分组成。

请求行:描述请求的基本信息
| 请求方法 | 说明 |
|---|---|
| GET | 请求服务器发送某个资源 |
| POST | 用来传输实体的主体 |
| PUT | 用来传输文件 |
| HEAD | 获取报文首部,用于确认URI的有效性及资源更新的日期时间等 |
| DELETE | 删除文件 |
| OPTIONS | 查询针对请求URI指定的资源支持的方法 |
| TRACE | 用于追踪路径 |
| CONNECT | 要求与代理服务器通信时建立隧道,实现用隧道协议进行TCP通信 |
| HTTP版本 | 说明 |
|---|---|
| HTTP/0.9 | 1991年制定,只支持GET方法 |
| HTTP/1.0 | 1996年诞生,增加了POST,HEAD方法 |
| HTTP/1.1 | 1999年发布并成为标准,增加了PUT方法,并允许持久连接 |
头部字段集合:使用key-value形式更详细地说明报文。主要分为四类:通用首部,请求首部,响应首部,实体首部
| 首部字段名 | 说明 |
|---|---|
| CacheControl | 控制缓存的行为 |
| Connection | 允许客户端和服务端指定与请求/响应连接有关的选项 |
| Date | 创建报文的日期时间 |
| Pragma | 另一种随报文传送指示的方式,但并不专用于缓存 |
| Transfer-Encoding | 告知接收端为了保证报文的可靠传输,对报文采用了什么编码方式 |
| Trailer | 报文末端的首部一览 |
| Update | 给出了发送端可能想要"升级"使用的新版本或协议 |
| Via | 显示了报文经过的中间节点(代理,网关) |
| 首部字段名 | 说明 |
|---|---|
| Accept | 告诉服务器能够发送哪些媒体类型 |
| Accept-Charset | 告诉服务器能够发送哪些字符集 |
| Accept-Encoding | 告诉服务器能够发送哪些编码方式 |
| Accept-Language | 告诉服务器能够发送哪些语言 |
| Authorization | 包含了客户端提供给服务器,以便对其自身进行认证的数据 |
| From | 提供了客户端用户的E-mail地址 |
| Host | 给出了接收请求的服务器的主机名和端口号 |
| If-Match | 如果实体标记与文档当前的实体标记相匹配,就获取这份文档 |
| If-Modified-Since | 除非在某个指定的日期之后资源被修改过,否则就限制这个请求 |
| If-None-Match | 如果提供的实体标记与当前文档的实体标记不相符,就获取文档 |
| If-Range | 允许对文档的某个范围进行条件请求 |
| If-Unmodified-Since | 除非在某个指定日期之后资源没有被修改过,否则就限制这个请求 |
| Max-Forward | 将请求转发给其他代理或网关的最大次数 |
| Proxy-Authorization | 与Authorization首部相同,但这个首部实在与代理进行认证时使用 |
| Range | 如果服务器支持范围请求,就请求资源的指定范围 |
| Referer | 提供包含当前请求URI的文档的URL |
| TE | 告诉服务器可以使用哪些扩展传输编码 |
| User-Agent | 将发起请求的应用程序名称告知服务器 |
| 首部字段名 | 说明 |
|---|---|
| Allow | 资源可支持的HTTP方法 |
| Content-Encoding | 实体主体适用的编码方式 |
| Content-Language | 实体主体的自然语言 |
| Content-Length | 实体主体的大小(单位:字节) |
| Content-Location | 替代对应资源的URI |
| Content-MD5 | 实体主体的报文摘要 |
| Content-Range | 实体主体的位置范围 |
| Content-Type | 实体主体的媒体类型 |
| Expires | 实体主体过期的日期时间 |
| Last-Modified | 资源的最后修改日期 |
消息正文:实际传输的数据,可以是纯文本,也可以是图片、视频等二进制数据
响应报文:由响应行,头部字段集合,消息正文三大部分组成。

| 状态码 | 类别 | 原因短语 |
|---|---|---|
| 1XX | 信息性状态码 | 接收的请求正在处理 |
| 2XX | 成功状态码 | 请求正常处理完毕 |
| 3XX | 重定向状态码 | 需要进行附加操作以完成请求 |
| 4XX | 客户端错误状态码 | 服务端无法处理请求 |
| 5XX | 服务端错误状态码 | 服务器处理请求出错 |
| 错误码 | 错误码描述 | 详细描述 |
|---|---|---|
| 200 | OK | 表示从客户端发来的请求在服务端被正常处理了 |
| 204 | No Content | 无内容。服务器成功处理,但未返回内容 |
| 206 | Partial Content | 部分内容。服务器成功处理了部分GET请求 |
| 301 | Moved Permanently | 永久重定向,意思是本地请求的资源以及不存在,使用新的URI再次访问 |
| 302 | Found | 临时重定向,临时则所请求的资源暂时还在,但是目前需要用另一个URI访问 |
| 303 | See Other | 与301类似,使用GET和POST请求查看 |
| 304 | Not Modified | 运用于缓存控制。它用于 If-Modified-Since 等条件请求,表示资源未修改,可以理解成"重定向已到缓存的文件" |
| 307 | Temporary Redirect | 临时重定向,与302类似,使用GET请求重定向 |
| 400 | Bad Request | 客户端请求的语法错误,服务器无法理解 |
| 401 | Unauthorized | 表示发送的请求需要有通过HTTP认证的认证信息 |
| 403 | Forbidden | 这一个是表示服务器禁止访问资源。原因比如涉及到敏感词汇、法律禁止等 |
| 404 | Not Found | 服务器无法根据客户端的请求找到资源 |
| 500 | Internal Server Error | 服务器内部错误,无法完成请求 |
| 503 | Service Unavailable | 表示服务器当前很忙,暂时无法响应服务,我们上网时有时候遇到的"网络服务正忙,请稍后重试"的提示信息就是状态码 503 |
| 首部字段名 | 说明 |
|---|---|
| Accept-Ranges | 对此资源来说,服务器可接受的范围类型 |
| Age | 响应持续时间 |
| ETag | 资源的匹配信息 |
| Location | 令客户端重定向至指定URI |
| Proxy-Authenticate | 代码服务器对客户端的认证信息 |
| Retry-After | 如果资源不可用,在此日期或时间重试 |
| Server | 服务器应用程序软件的名称和版本 |
| Vary | 代理服务器缓存的管理信息 |
| WWW-Authenticate | 服务器对客户端的认证信息 |
#include "httpd.h"
void threadFunc(void* arg, int conn){
Httpd* httpd = (Httpd*)arg;
// 接收http请求
char bodyBuf[1024] = {0};
int recvSize = recv(conn, bodyBuf, sizeof(bodyBuf), 0);
printf("%s\n", bodyBuf);
std::string strMethod;
std::string strUri;
std::string strVersion;
std::map<std::string, std::string> requestHead;
std::string requestBody;
// 解析http请求,包括请求方式(目前只支持GET和POST请求),URI,http版本
httpd->parseHttpRequestInfo(bodyBuf, strMethod, strUri, strVersion);
// 解析http请求头
httpd->parseHttpRequestHead(bodyBuf, requestHead);
//解析http请求体
httpd->parseHttpRequestBody(bodyBuf, requestBody);
//根据不同请求方式进行响应
if(strMethod.compare("GET") == 0){
httpd->httpResponseHtml(conn);
}else if(strMethod.compare("POST") == 0){
std::string data1;
std::string data2;
if(httpd->parseBodyJson(requestBody, data1, data2)){
httpd->httpResponseJson(conn, data1, data2);
}
}
//关闭套接字
close(conn);
}
bool Httpd::start(){
//定义sockfd
int server_sockfd = socket(AF_INET,SOCK_STREAM, 0);
///定义sockaddr_in
struct sockaddr_in server_sockaddr;
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(4000);
server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
//bind,成功返回0,出错返回-1
if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1){
perror("bind");
return false;
}
//listen,成功返回0,出错返回-1
if(listen(server_sockfd, 5) == -1){
perror("listen");
return false;
}
//客户端套接字
char buffer[1024] = {0};
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
int conn = 0;
while(1){
//成功返回非负描述字,出错返回-1
conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length);
if(conn < 0){
perror("connect");
return false;
}
//开启线程处理请求
std::thread th;
th = std::thread(threadFunc, this, conn);
th.join();
}
close(server_sockfd);
return true;
}
bool Httpd::parseHttpRequestInfo(std::string httpRequest, std::string& method, std::string& uri, std::string& version){
int recvSize = httpRequest.size();
//查找请求头
std::string strRequestHead;
int pos = httpRequest.find("\r\n");
strRequestHead = httpRequest.substr(0, pos);
//解析请求类型
method = strRequestHead.substr(0, strRequestHead.find(" "));
//解析uri
uri = strRequestHead.substr(strRequestHead.find(" ") + 1, strRequestHead.find(" ", strRequestHead.find(" ") + 1) - strRequestHead.find(" "));
//解析http版本
version = strRequestHead.substr(strRequestHead.rfind(" "), strRequestHead.size() - strRequestHead.rfind(" "));
return true;
}
bool Httpd::parseHttpRequestHead(std::string httpRequest, std::map<std::string, std::string>& requestHead){
int recvSize = httpRequest.size();
int headPos = httpRequest.find("\r\n");
int bodySize = parseBodySize(httpRequest);
std::string strRequestH;
do{
int iPos = httpRequest.find("\r\n", headPos + strlen("\r\n"));
strRequestH = httpRequest.substr(headPos, iPos - headPos);
if(strRequestH.find(":") != std::string::npos){
std::string strKey = strRequestH.substr(0, strRequestH.find(":"));
std::string strValue = strRequestH.substr(strRequestH.find(":") + 1, strRequestH.size() - strRequestH.find(":"));
requestHead.insert(std::pair<std::string, std::string>(strKey, strValue));
}
headPos = iPos;
} while(headPos < recvSize - bodySize && headPos > 0);
return true;
}
bool Httpd::parseHttpRequestBody(std::string httpRequest, std::string& requestBody){
int recvSize = httpRequest.size();
int bodySize = parseBodySize(httpRequest);
if(bodySize == 0){
return false;
}
requestBody = httpRequest.substr(recvSize - bodySize, bodySize);
return true;
}
int Httpd::parseBodySize(std::string httpRequest){
std::string strContentLength;
int posLengthStart = httpRequest.find("Content-Length: ") + strlen("Content-Length: ");
int posLengthEnd = httpRequest.find("\r\n", httpRequest.find("Content-Length: ") + strlen("Content-Length: "));
strContentLength = httpRequest.substr(posLengthStart, posLengthEnd - posLengthStart);
return atoi(strContentLength.c_str());
}







我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub
我正在使用puppet为ruby程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这
是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou
我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是
我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search
最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru
我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m
在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo