When people request a servlet that is not found on csp folder
it will show a "404 Not Found" Response
Not Found
The requested URL was not found on this server.
is there a way we can check a servlet exists or not, to create custom 404 page?
Just like Gil said. You can use HDL_HTTP_ERRORS to intercept HTTP errors. To make it more clear here is a sample connection handler that replaces 404 error with a custom error message.
#include "gwan.h"
int init(int argc, char *argv[])
{
u32 *states = (u32*)get_env(argv, US_HANDLER_STATES);
*states = (1 << HDL_HTTP_ERRORS);
return 0;
}
int main(int argc, char *argv[])
{
if((long)argv[0] != HDL_HTTP_ERRORS)
return 255; // Continue if not an error
// get the HTTP reply code
int *status = (int*)get_env(argv, HTTP_CODE);
if(!status || *status != 404)
return 255; // Continue if not a 404 error
static char custom_err[] =
"<!DOCTYPE HTML><html><head><title>404 Not Found</title><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><link href=\"/imgs/errors.css\" rel=\"stylesheet\" type=\"text/css\"></head>"
"<body><h1>Not Found</h1>"
"<p>This is a custom 404 not found error. What makes you think that this link exist?!!</p></body></html>";
static char header[] =
"HTTP/1.1 %s\r\n"
"Server: G-WAN\r\n"
"Date: %s\r\n"
"Content-Type: text/html; charset=UTF-8\r\n"
"Content-Length: %u\r\n\r\n";
int len = sizeof(custom_err)-1;
char *date = (char*)get_env(argv, SERVER_DATE);
// Set our http reply headers
build_headers(argv, header,
http_status(*status),
date, // current server date
len); // Reply length
// Set our reply using our custom error
set_reply(argv, custom_err, len, *status);
return 2; // End request then send reply
}
void clean(int argc, char *argv[]) {}
Take note if you are returning a 404 error from a servlet. Make sure you do a
xbuf_empty(get_reply(argv));
to empty the contents of the reply buffer. It will not reach HDL_HTTP_ERRORS if there are any content on the reply buffer. It will just reply with whatever the reply buffer has.
Both Content-Type and Connections handlers can check if the resource exists before serving it.
But the HDL_HTTP_ERRORS state of Connection Handlers lets you intercept HTTP errors to alter the default reply otherwise generated by G-WAN. It is defined in the documented G-WAN API Handler States.
That's most probably what you are looking for.
Related
This question already has an answer here:
Differ between header and content of http server response (sockets)
(1 answer)
Closed 1 year ago.
I'm making this socket HTTP client (very basic). When recv()'ing response data from example.com it works fine and writes it all to a buffer but when I try to revc any bigger amounts of data it stops at around 1500 bytes.
Right now all I'm trying to do is get the response written into the buffer (headers and all). Not trying to parse anything. But that isn't working. It works for a few iterations but then stops or hangs. I'm asking for help identifying the issue with this receive_response() function that causes these behaviors.
This is the function that revc's the HTTP response:
void tcp_client::receive_response(char *buffer) {
int bytes_recv = 0;
int total_bytes_recv = 0;
for (;;) {
bytes_recv = recv(sock, &buffer[total_bytes_recv], CHUNK_SIZE, 0);
if (bytes_recv <= 0) {
break;
} else {
total_bytes_recv += bytes_recv;
}
}
}
The main function:
int main(int argc, char **argv) {
http_client http;
char response[100000] = {0};
http.connect_to_host("go.com", 80);
http.send_request("GET / HTTP/1.1\r\n\r\n");
http.receive_response(response);
std::cout << response << std::endl;
return 0;
}
Thank you
You seem to expect the server to close the connection after the response is transmitted. A typical HTTP 1.1 server doesn't do that by default; they keep the connection open for further requests, unless the client explicitly asks otherwise via Connection: close header.
So, you receive all the data, and then the next recv call is sitting there, waiting for more data to arrive.
An HTTP 1.1 client is expected to detect the end of response via Content-Length header, or by decoding a chunked response as indicated by Transfer-Encoding: chunked header.
I am trying to set a mongoose web server v3.3 with a self-signed SSL certificate. I know how to do it without SSL but I want to implement HTTPS.
I have implemented something like this:
void *event_handler(enum mg_event event,
struct mg_connection *conn) {
const struct mg_request_info *request_info = mg_get_request_info(conn);
static void* done = "done";
if (event == MG_NEW_REQUEST) {
if (strcmp(request_info->uri, "/hello") == 0) {
// handle c[renderer] request
if(strcmp(request_info->request_method, "GET") != 0) {
// send error (we only care about HTTP GET)
mg_printf(conn, "HTTP/1.1 %d Error (%s)\r\n\r\n%s",
500,
"we only care about HTTP GET",
"we only care about HTTP GET");
// return not null means we handled the request
return done;
}
// handle your GET request to /hello
char* content = "Hello World!";
char* mimeType = "text/plain";
int contentLength = strlen(content);
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Cache: no-cache\r\n"
"Content-Type: %s\r\n"
"Content-Length: %d\r\n"
"\r\n",
mimeType,
contentLength);
mg_write(conn, content, contentLength);
return done;
}
}
// in this example i only handle /hello
mg_printf(conn, "HTTP/1.1 %d Error (%s)\r\n\r\n%s",
500, /* This the error code you want to send back*/
"Invalid Request.",
"Invalid Request.");
return done;
}
// No suitable handler found, mark as not processed. Mongoose will
// try to serve the request.
return NULL;
}
int main(int argc, char **argv) {
const char *options[] = {
"ssl_certificate", "cert.pem",
"listening_ports", "443s",
"num_threads", "10",
NULL
};
static struct mg_context *ctx;
ctx = mg_start(&event_handler, options);
if(ctx == NULL) {
exit(EXIT_FAILURE);
}
puts("Server running, press enter to exit\n");
getchar();
mg_stop(ctx);
return EXIT_SUCCESS;
}
The problem is I am not able to access my server from the web browser. I think the problem is that the first event my callback receives is MG_INIT_SSL but I do not know how to handle or process it. Could anybody please help?
Firstly, I believe you should not have to handle other events than MG_NEW_REQUEST in your event handler.
I would also debug using openssl:
openssl s_client -connect <hostname:port>
to see that the SSL connection gets set up properly.
In any case, Cesanta does provide a complete working example for you to use:
https://github.com/cesanta/mongoose/tree/master/examples/simplest_web_server_ssl
I am trying to send a get request to acounts.google.com to be able to implement a library for C++ OAuth to learn it.
I get the following code from this post: Creating a HTTPS request using Boost Asio and OpenSSL and modified it as follow:
int main()
{
try
{
std::string request = "/o/oauth2/v2/auth";
boost::system::error_code ec;
using namespace boost::asio;
// what we need
io_service svc;
ssl::context ctx(svc, ssl::context::method::sslv23_client);
ssl::stream<ip::tcp::socket> ssock(svc, ctx);
ip::tcp::resolver resolver(svc);
auto it = resolver.resolve({ "accounts.google.com", "443" }); // https://accouts.google.com:443
boost::asio::connect(ssock.lowest_layer(), it);
ssock.handshake(ssl::stream_base::handshake_type::client);
// send request
std::string fullResuest = "GET " + request + " HTTP/1.1\r\n\r\n";
boost::asio::write(ssock, buffer(fullResuest));
// read response
std::string response;
do
{
char buf[1024];
size_t bytes_transferred = ssock.read_some(buffer(buf), ec);
if (!ec) response.append(buf, buf + bytes_transferred);
std::cout << "Response received: '" << response << "'\n"; // I add this to see what I am getting from the server, so it should not be here.
} while (!ec);
// print and exit
std::cout << "Response received: '" << response << "'\n";
}
catch (const std::exception& e)
{
std::cerr << e.what() << std::endl;
if (std::string const * extra = boost::get_error_info<my_tag_error_info>(e))
{
std::cout << *extra << std::endl;
}
}
}
The problem that I have is as follow:
1- The results that I am getting is not what I am getting when I visit https://accounts.google.com/o/oauth2/v2/auth using a web browser. I essentially getting a message that they can not find the requested URL /o/oauth2/v2/auth
<p>The requested URL <code>/o/oauth2/v2/auth</code> was not found on this server. <ins>ThatÔÇÖs all we know.</ins>
How should I setup the GET commend so I can get the same result that I am getting with a browser?
2- The application hangs getting data from server, apparently the following loop is not right:
do
{
char buf[1024];
size_t bytes_transferred = ssock.read_some(buffer(buf), ec);
if (!ec) response.append(buf, buf + bytes_transferred);
} while (!ec);
What is the correct way of reading responce from the web server which is fast and read all data?
Edit 1
For reference based on accepted answer, I fixed the problem using the correct GET header as shown below:
// send request
std::string fullResuest = "GET " + request + " HTTP/1.1\r\n";
fullResuest+= "Host: " + server + "\r\n";
fullResuest += "Accept: */*\r\n";
fullResuest += "Connection: close\r\n\r\n";
boost::asio::write(ssock, buffer(fullResuest));
A HTTP/1.1 request must have a Host header. A simple experiment with OpenSSL will show the problem, i.e. the missing header:
$ openssl s_client -connect accounts.google.com:443
...
GET /o/oauth2/v2/auth HTTP/1.1
... The requested URL <code>/o/oauth2/v2/auth</code> was not found on this server. <ins>That’s all we know.</ins>
When adding the Host header instead we get a different response:
$ openssl s_client -connect accounts.google.com:443
...
GET /o/oauth2/v2/auth HTTP/1.1
Host: accounts.google.com
... >Required parameter is missing: response_type<
Apart from that HTTP/1.1 implicitly uses HTTP keep-alive, i.e. server and client might keep the connection open after the response is done. This means you should not read until the end of connection but should instead properly parse the HTTP header, extract the Content-length header and/or Transfer-Encoding header and behave according to their values. Or if you want it simpler use HTTP/1.0 instead.
For more information see the HTTP/1.1 standard.
I have a problem in my code I did send HTTP request to facebook server but the problem i get HTTP/1.1 301 Moved Permanently
message.
What i want to do is to allow follow the header response link and give me the redirected page HTML,
Also I tried to send another HTTP request but I get no response so i thought there is function option that allow me to follow the header link.
So basically what i'm trying to do is this send http post request to facebook server with my user and password log in follow header redirect link give me response.
if it's possible please guide me to the right way to do it.
PS: I already read some RFC and it's still not clear.
Thanks for your time.
My code
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
using namespace std;
int main()
{
int sockid, status;
unsigned strSize;
ssize_t count; //
struct sockaddr_in addr; // struct
ssize_t size = sizeof(addr);
sockid = socket(PF_INET, SOCK_STREAM, 0); // create socket
if(sockid < 0) //if error
{
cout<<"Cannot create sucket\n";
close(sockid);
exit(EXIT_FAILURE);
}
// access struct
addr.sin_family = AF_INET;
addr.sin_port = htons(80);
if(inet_aton("31.13.90.36",&addr.sin_addr) == 0)
{
cout << "Wrong address";
close(sockid);
exit(-1);
}
status = connect(sockid, (sockaddr*)&addr, sizeof(addr)); // attempt to establish a connection
// check
if(status < 0)
{
cout << "failed to establish a connection";
close(sockid);
exit(EXIT_FAILURE);
}
// sending HTTP request
char msg[] = "POST /login.php HTTP/1.1\r\n"
"HOST: m.facebook.com\r\n"
"Content-type: application/x-www-form-urlencoded\r\n"
"Content-Length: 147\r\n"
"Connection: Keep-Alive\r\n"
"\r\n"
"lsd=AVp5UV4F&version=1&ajax=0&width=0&pxr=0&gps=0&dimensions=0&m_ts=1481464104&li=KFlNWFL78UFJkrUnTV_sFFDQ&email=Minglixe&pass=test123&login=Log+In";
const int bufSize = 4000;
char * buf;
buf = new char [bufSize];
//unsigned bufSize = strlen(buf);
strSize = strlen(msg);
send(sockid, msg, strSize, 0); // send request
while((count = recv(sockid, buf, bufSize, 0)) > 0)// receive the request
{
buf[count] = '\0';
cout << buf << flush;
}
if(count < 0 )
{
cout << "error" ;
exit(EXIT_FAILURE);
}
delete[] buf;
close(sockid);
}
You will have to issue another HTTP request, a GET request, to the redirected URL given in the 301 response.
You will need to close this socket, and create a new socket for the new request. If the redirected URL is for the same domain, you could implement HTTP/1.1 pipelining and save yourself the trouble of tearing down one socket and creating a new one, but that would be overkill.
In addition, it is a near certainty that the 301 response also gave you a handful of cookies, which Facebook's server will expect you to return to it, in the redirected request. Otherwise none of what you're trying to do will have any chance of working.
Unfortunately, there are no shortcuts here, and no instant gratification. In order to do this correctly, you need to understand, and be familiar with, pretty much, the entirety of HTTP. The canonical reference for HTTP consists of RFC 7230 and RFC 7231 (there's also RFC 7232, RFC 7233, RFC 7234, RFC 7235, RFC 7236 and RFC 7237, but I don't believe you need them for your particular purpose). Rather than copy-pasting chunks of it here, I will direct you to the authoritative source, in RFC 7230 and 7231.
HTTP is not really that complicated, but it is not something that can be absorbed in a few hours, like POP3. Be prepared to invest a few days on reviewing that technical specification for HTTP, and studying it. When I implemented my own full-featured HTTP 1.1 client, it took me (in my spare time), a few months to work my way through the RFC 2616 predecessor, and hack up a cookie-supporting HTTP 1.1 client.
Furthermore, in order for your HTTP client to handle cookies, the canonical reference for how to handle cookies is RFC 6265. You will need to learn that too.
P.S. Hopefully the redirected URL is not an https URL, or Facebook permits non-encrypted connections to fetch content after logging in. Otherwise, I'm afraid, you will need to pull in your platform's SSL libraries, and also code a TLS/SSL session on top of the whole thing.
There is a server that response a .png file over http:
#include "server.h"
string Server::header(int contentLength)
{
string h =
"HTTP/1.1 200 OK\n"
"Content-Length: " + boost::lexical_cast<string>(contentLength) + "\n"
"Content-Type: image/png;\n"
"Connection: close\n"
"\n";
return h;
}
string Server::readMap(const string &filename)
{
ifstream file (filename.c_str(), ios::in|ios::binary);
string reply;
char buf[512];
while (file.read(buf, sizeof(buf)).gcount() > 0)
reply.append(buf, file.gcount());
return reply;
}
void Server::run(const string &filename, int port)
{
string data = readMap(filename);
try
{
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), port));
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
boost::asio::write(socket, boost::asio::buffer(header( data.size() )));
boost::asio::write(socket, boost::asio::buffer(data));
}
}
catch (std::exception& e)
{
cerr << "exception: " << e.what() << endl;
}
}
Every time an error happens:
exception: Connection reset by peer
I can see some part of an image in my browser, and sometimes the image is almost complete, but it never works without the error.
If I use wget it looks like
wget http://localhost:8089
--2012-03-07 12:07:19-- http://localhost:8089/
Resolving localhost... 127.0.0.1
Connecting to localhost|127.0.0.1|:8089... connected.
HTTP request sent, awaiting response... 200 OK
Length: 760032 (742K) [image/png]
Saving to: `index.html'
62% [========================================================> ] 475,136 --.-K/s in 0.002s
2012-03-07 12:07:19 (287 MB/s) - Read error at byte 475136/760032 (Connection reset by peer). Retrying.
--2012-03-07 12:07:20-- (try: 2) http://localhost:8089/
Connecting to localhost|127.0.0.1|:8089... connected.
HTTP request sent, awaiting response... 200 OK
Length: 760032 (742K) [image/png]
Saving to: `index.html'
73% [==================================================================> ] 557,056 --.-K/s in 0.001s
... many failes and finally
--2012-03-07 12:09:01-- (try: 9) http://localhost:8089/
Connecting to localhost|127.0.0.1|:8089... connected.
HTTP request sent, awaiting response... 200 OK
Length: 760032 (742K) [image/png]
Saving to: `index.html'
100%[===========================================================================================>] 760,032 --.-K/s in 0.001s
Any ideas how to fix it?
There are several more complete HTTP-implementations in the ASIO-docs, including static file serving. One way to go would be to reuse some of that sample code for your application.
In this particular case, there's an example of how to correctly open and buffer a file at http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/example/http/server/request_handler.cpp
std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
...
char buf[512];
while (is.read(buf, sizeof(buf)).gcount() > 0)
rep.content.append(buf, is.gcount());
The docs also has examples for actual asynchronous HTTP-implementations. (I assume you're using boost::asio to eventually make it asynchronous?)
You should receive and decode the HTTP request first, and only send the content if that was what was requested. Browsers sometimes request other resources as well; they may get upset if you send something unexpected, or if you send it before they've sent the request.
You also seem to have an off-by-one error in the data size - you put data.size()-1 in the header, and then send all of data. Perhaps this is a partial workaround for the bug in readMap, where you push an extra character after reaching EOF. You would be better off fixing that, by checking for eof() after reading but before pushing the character; or by reading in a less error-prone (and more efficient) manner, such as:
std::copy(std::istreambuf_iterator<char>(file),
std::istreambuf_iterator<char>(),
std::back_inserter(data));
Also, I don't see any reason to copy the vector into a string. vector can also be converted to an asio::buffer.
Your way of reading the file is incorrect for a start.
Not simply that reading one character at a time isn't a great idea, but the loop is wrong. You could use istreambuf_iterator<char> to input or read() with a number of characters with gcount() determining when the read is complete.